diff options
Diffstat (limited to 'sc/source/core/data')
64 files changed, 73381 insertions, 0 deletions
diff --git a/sc/source/core/data/attarray.cxx b/sc/source/core/data/attarray.cxx new file mode 100644 index 000000000000..1a466d231459 --- /dev/null +++ b/sc/source/core/data/attarray.cxx @@ -0,0 +1,2614 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: attarray.cxx,v $ + * $Revision: 1.25.32.2 $ + * + * 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 "scitems.hxx" +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/bolnitem.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/shaditem.hxx> +#include <svtools/poolcach.hxx> +#include <svx/fontitem.hxx> +#include <vcl/fontcvt.hxx> + +#include "attarray.hxx" +#include "global.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "markarr.hxx" +#include "rechead.hxx" +#include "globstr.hrc" + + +#undef DBG_INVALIDATE +#define DBGOUTPUT(s) \ + DBG_ERROR( String("Invalidate ") + String(s) + String(": ") \ + + String(nCol) + String('/') + String(aAdrStart.Row()) + String('/') + String(nTab) \ + + String(" bis ") \ + + String(nCol) + String('/') + String(aAdrEnd.Row()) + String('/') + String(nTab) \ + ); + +// STATIC DATA ----------------------------------------------------------- + + +//------------------------------------------------------------------------ + +ScAttrArray::ScAttrArray( SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc ) : + nCol( nNewCol ), + nTab( nNewTab ), + pDocument( pDoc ) +{ + nCount = nLimit = 1; + pData = new ScAttrEntry[1]; + if (pData) + { + pData[0].nRow = MAXROW; + pData[0].pPattern = pDocument->GetDefPattern(); // ohne Put !!! + } +} + +//------------------------------------------------------------------------ + +ScAttrArray::~ScAttrArray() +{ +#ifdef DBG_UTIL + TestData(); +#endif + + if (pData) + { + ScDocumentPool* pDocPool = pDocument->GetPool(); + for (SCSIZE i=0; i<nCount; i++) + pDocPool->Remove(*pData[i].pPattern); + + delete[] pData; + } +} + +//------------------------------------------------------------------------ +#ifdef DBG_UTIL +void ScAttrArray::TestData() const +{ + + USHORT nErr = 0; + if (pData) + { + SCSIZE nPos; + for (nPos=0; nPos<nCount; nPos++) + { + if (nPos > 0) + if (pData[nPos].pPattern == pData[nPos-1].pPattern || pData[nPos].nRow <= pData[nPos-1].nRow) + ++nErr; + if (pData[nPos].pPattern->Which() != ATTR_PATTERN) + ++nErr; + } + if ( nPos && pData[nPos-1].nRow != MAXROW ) + ++nErr; + } + if (nErr) + { + ByteString aMsg = ByteString::CreateFromInt32(nErr); + aMsg += " errors in attribute array, column "; + aMsg += ByteString::CreateFromInt32(nCol); + DBG_ERROR( aMsg.GetBuffer() ); + } +} +#endif + +//------------------------------------------------------------------------ + +void ScAttrArray::Reset( const ScPatternAttr* pPattern, BOOL bAlloc ) +{ + if (pData) + { + ScDocumentPool* pDocPool = pDocument->GetPool(); + const ScPatternAttr* pOldPattern; + ScAddress aAdrStart( nCol, 0, nTab ); + ScAddress aAdrEnd ( nCol, 0, nTab ); + + for (SCSIZE i=0; i<nCount; i++) + { + // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert + pOldPattern = pData[i].pPattern; + BOOL bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + pPattern->GetItemSet(), pOldPattern->GetItemSet() ) ) + { + aAdrStart.SetRow( i ? pData[i-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[i].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); +#ifdef DBG_INVALIDATE + DBGOUTPUT("Reset"); +#endif + } + // bedingtes Format gesetzt oder geloescht? + if ( &pPattern->GetItem(ATTR_CONDITIONAL) != &pOldPattern->GetItem(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + pOldPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ); + } + pDocPool->Remove(*pOldPattern); + } + delete[] pData; + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, FALSE); + + if (bAlloc) + { + nCount = nLimit = 1; + pData = new ScAttrEntry[1]; + if (pData) + { + ScPatternAttr* pNewPattern = (ScPatternAttr*) &pDocPool->Put(*pPattern); + pData[0].nRow = MAXROW; + pData[0].pPattern = pNewPattern; + } + } + else + { + nCount = nLimit = 0; + pData = NULL; // muss sofort wieder belegt werden ! + } + } +} + + +BOOL ScAttrArray::Concat(SCSIZE nPos) +{ + BOOL bRet = FALSE; + if (pData && (nPos < nCount)) + { + if (nPos > 0) + { + if (pData[nPos - 1].pPattern == pData[nPos].pPattern) + { + pData[nPos - 1].nRow = pData[nPos].nRow; + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + memmove(&pData[nPos], &pData[nPos + 1], (nCount - nPos - 1) * sizeof(ScAttrEntry)); + pData[nCount - 1].pPattern = NULL; + pData[nCount - 1].nRow = 0; + nCount--; + nPos--; + bRet = TRUE; + } + } + if (nPos + 1 < nCount) + { + if (pData[nPos + 1].pPattern == pData[nPos].pPattern) + { + pData[nPos].nRow = pData[nPos + 1].nRow; + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + memmove(&pData[nPos + 1], &pData[nPos + 2], (nCount - nPos - 2) * sizeof(ScAttrEntry)); + pData[nCount - 1].pPattern = NULL; + pData[nCount - 1].nRow = 0; + nCount--; + bRet = TRUE; + } + } + } + return bRet; +} + +//------------------------------------------------------------------------ + +BOOL ScAttrArray::Search( SCROW nRow, SCSIZE& nIndex ) const +{ + long nLo = 0; + long nHi = static_cast<long>(nCount) - 1; + long nStartRow = 0; + long nEndRow = 0; + long i = 0; + BOOL bFound = (nCount == 1); + if (pData) + { + while ( !bFound && nLo <= nHi ) + { + i = (nLo + nHi) / 2; + if (i > 0) + nStartRow = (long) pData[i - 1].nRow; + else + nStartRow = -1; + nEndRow = (long) pData[i].nRow; + if (nEndRow < (long) nRow) + nLo = ++i; + else + if (nStartRow >= (long) nRow) + nHi = --i; + else + bFound = TRUE; + } + } + else + bFound = FALSE; + + if (bFound) + nIndex=(SCSIZE)i; + else + nIndex=0; + return bFound; +} + + +const ScPatternAttr* ScAttrArray::GetPattern( SCROW nRow ) const +{ + SCSIZE i; + if (Search( nRow, i )) + return pData[i].pPattern; + else + return NULL; +} + + +const ScPatternAttr* ScAttrArray::GetPatternRange( SCROW& rStartRow, + SCROW& rEndRow, SCROW nRow ) const +{ + SCSIZE nIndex; + if ( Search( nRow, nIndex ) ) + { + if ( nIndex > 0 ) + rStartRow = pData[nIndex-1].nRow + 1; + else + rStartRow = 0; + rEndRow = pData[nIndex].nRow; + return pData[nIndex].pPattern; + } + return NULL; +} + +//------------------------------------------------------------------------ + +void ScAttrArray::SetPattern( SCROW nRow, const ScPatternAttr* pPattern, BOOL bPutToPool ) +{ + SetPatternArea( nRow, nRow, pPattern, bPutToPool ); +} + + +void ScAttrArray::SetPatternArea(SCROW nStartRow, SCROW nEndRow, const ScPatternAttr *pPattern, BOOL bPutToPool ) +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + if (bPutToPool) + pPattern = (const ScPatternAttr*) &pDocument->GetPool()->Put(*pPattern); + + if ((nStartRow == 0) && (nEndRow == MAXROW)) + Reset(pPattern); + else + { + SCSIZE nNeeded = nCount + 2; + if ( nLimit < nNeeded ) + { + nLimit += SC_ATTRARRAY_DELTA; + if ( nLimit < nNeeded ) + nLimit = nNeeded; + ScAttrEntry* pNewData = new ScAttrEntry[nLimit]; + memcpy( pNewData, pData, nCount*sizeof(ScAttrEntry) ); + delete[] pData; + pData = pNewData; + } + + ScAddress aAdrStart( nCol, 0, nTab ); + ScAddress aAdrEnd ( nCol, 0, nTab ); + + SCSIZE ni = 0; // number of entries in beginning + SCSIZE nx = 0; // track position + SCROW ns = 0; // start row of track position + if ( nStartRow > 0 ) + { + // skip beginning + SCSIZE nIndex; + Search( nStartRow, nIndex ); + ni = nIndex; + + if ( ni > 0 ) + { + nx = ni; + ns = pData[ni-1].nRow+1; + } + } + + // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert + // oder bedingte Formate neu gesetzt oder geloescht werden + while ( ns <= nEndRow ) + { + const SfxItemSet& rNewSet = pPattern->GetItemSet(); + const SfxItemSet& rOldSet = pData[nx].pPattern->GetItemSet(); + + BOOL bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( Max(nStartRow,ns) ); + aAdrEnd .SetRow( Min(nEndRow,pData[nx].nRow) ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); +#ifdef DBG_INVALIDATE + DBGOUTPUT("SetPatternArea"); +#endif + } + if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rOldSet.Get(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rNewSet.Get(ATTR_CONDITIONAL)).GetValue() ); + } + ns = pData[nx].nRow + 1; + nx++; + } + + // continue modifying data array + + SCSIZE nInsert; // insert position (MAXROWCOUNT := no insert) + BOOL bCombined = FALSE; + BOOL bSplit = FALSE; + if ( nStartRow > 0 ) + { + nInsert = MAXROWCOUNT; + if ( pData[ni].pPattern != pPattern ) + { + if ( ni == 0 || (pData[ni-1].nRow < nStartRow - 1) ) + { // may be a split or a simple insert or just a shrink, + // row adjustment is done further down + if ( pData[ni].nRow > nEndRow ) + bSplit = TRUE; + ni++; + nInsert = ni; + } + else if ( ni > 0 && pData[ni-1].nRow == nStartRow - 1 ) + nInsert = ni; + } + if ( ni > 0 && pData[ni-1].pPattern == pPattern ) + { // combine + pData[ni-1].nRow = nEndRow; + nInsert = MAXROWCOUNT; + bCombined = TRUE; + } + } + else + nInsert = 0; + + SCSIZE nj = ni; // stop position of range to replace + while ( nj < nCount && pData[nj].nRow <= nEndRow ) + nj++; + if ( !bSplit ) + { + if ( nj < nCount && pData[nj].pPattern == pPattern ) + { // combine + if ( ni > 0 ) + { + if ( pData[ni-1].pPattern == pPattern ) + { // adjacent entries + pData[ni-1].nRow = pData[nj].nRow; + nj++; + } + else if ( ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + nInsert = MAXROWCOUNT; + bCombined = TRUE; + } + else if ( ni > 0 && ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + ScDocumentPool* pDocPool = pDocument->GetPool(); + if ( bSplit ) + { // duplicate splitted entry in pool + pDocPool->Put( *pData[ni-1].pPattern ); + } + if ( ni < nj ) + { // remove middle entries + for ( SCSIZE nk=ni; nk<nj; nk++) + { // remove entries from pool + pDocPool->Remove( *pData[nk].pPattern ); + } + if ( !bCombined ) + { // replace one entry + pData[ni].nRow = nEndRow; + pData[ni].pPattern = pPattern; + ni++; + nInsert = MAXROWCOUNT; + } + if ( ni < nj ) + { // remove entries + memmove( pData + ni, pData + nj, (nCount - nj) * sizeof(ScAttrEntry) ); + nCount -= nj - ni; + } + } + + if ( nInsert < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + { // insert or append new entry + if ( nInsert <= nCount ) + { + if ( !bSplit ) + memmove( pData + nInsert + 1, pData + nInsert, + (nCount - nInsert) * sizeof(ScAttrEntry) ); + else + { + memmove( pData + nInsert + 2, pData + nInsert, + (nCount - nInsert) * sizeof(ScAttrEntry) ); + pData[nInsert+1] = pData[nInsert-1]; + nCount++; + } + } + if ( nInsert ) + pData[nInsert-1].nRow = nStartRow - 1; + pData[nInsert].nRow = nEndRow; + pData[nInsert].pPattern = pPattern; + nCount++; + } + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, FALSE); + } + } +// InfoBox(0, String(nCount) + String(" Eintraege") ).Execute(); + +#ifdef DBG_UTIL + TestData(); +#endif +} + + +void ScAttrArray::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, ScStyleSheet* pStyle ) +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + DBG_ERROR("Search-Fehler"); + return; + } + + ScAddress aAdrStart( nCol, 0, nTab ); + ScAddress aAdrEnd ( nCol, 0, nTab ); + + do + { + const ScPatternAttr* pOldPattern = pData[nPos].pPattern; + ScPatternAttr* pNewPattern = new ScPatternAttr(*pOldPattern); + pNewPattern->SetStyleSheet(pStyle); + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + nStart = pData[nPos].nRow + 1; + + if ( *pNewPattern == *pOldPattern ) + { + // keep the original pattern (might be default) + // pNewPattern is deleted below + nPos++; + } + else if ( nY1 < nStartRow || nY2 > nEndRow ) + { + if (nY1 < nStartRow) nY1=nStartRow; + if (nY2 > nEndRow) nY2=nEndRow; + SetPatternArea( nY1, nY2, pNewPattern, TRUE ); + Search( nStart, nPos ); + } + else + { + // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert + // bedingte Formate in Vorlagen gibt es (noch) nicht + + const SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + + BOOL bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[nPos].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); +#ifdef DBG_INVALIDATE + DBGOUTPUT("ApplyStyleArea"); +#endif + } + + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + if (Concat(nPos)) + Search(nStart, nPos); + else + nPos++; + } + delete pNewPattern; + } + while ((nStart <= nEndRow) && (nPos < nCount)); + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, FALSE); + } + +#ifdef DBG_UTIL + TestData(); +#endif +} + + + // const wird weggecastet, weil es sonst + // zu ineffizient/kompliziert wird! +#define SET_LINECOLOR(dest,c) \ + if ((dest)) \ + { \ + ((SvxBorderLine*)(dest))->SetColor((c)); \ + } + +#define SET_LINE(dest,src) \ + if ((dest)) \ + { \ + SvxBorderLine* pCast = (SvxBorderLine*)(dest); \ + pCast->SetOutWidth((src)->GetOutWidth()); \ + pCast->SetInWidth ((src)->GetInWidth()); \ + pCast->SetDistance((src)->GetDistance()); \ + } + +void ScAttrArray::ApplyLineStyleArea( SCROW nStartRow, SCROW nEndRow, + const SvxBorderLine* pLine, BOOL bColorOnly ) +{ + if ( bColorOnly && !pLine ) + return; + + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + DBG_ERROR("Search-Fehler"); + return; + } + + do + { + const ScPatternAttr* pOldPattern = pData[nPos].pPattern; + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + const SfxPoolItem* pBoxItem = 0; + SfxItemState eState = rOldSet.GetItemState( ATTR_BORDER, TRUE, &pBoxItem ); + const SfxPoolItem* pTLBRItem = 0; + SfxItemState eTLBRState = rOldSet.GetItemState( ATTR_BORDER_TLBR, TRUE, &pTLBRItem ); + const SfxPoolItem* pBLTRItem = 0; + SfxItemState eBLTRState = rOldSet.GetItemState( ATTR_BORDER_BLTR, TRUE, &pBLTRItem ); + + if ( (SFX_ITEM_SET == eState) || (SFX_ITEM_SET == eTLBRState) || (SFX_ITEM_SET == eBLTRState) ) + { + ScPatternAttr* pNewPattern = new ScPatternAttr(*pOldPattern); + SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + + SvxBoxItem* pNewBoxItem = pBoxItem ? (SvxBoxItem*)pBoxItem->Clone() : 0; + SvxLineItem* pNewTLBRItem = pTLBRItem ? (SvxLineItem*)pTLBRItem->Clone() : 0; + SvxLineItem* pNewBLTRItem = pBLTRItem ? (SvxLineItem*)pBLTRItem->Clone() : 0; + + // Linienattribute holen und mit Parametern aktualisieren + + if ( !pLine ) + { + if( pNewBoxItem ) + { + if ( pNewBoxItem->GetTop() ) pNewBoxItem->SetLine( NULL, BOX_LINE_TOP ); + if ( pNewBoxItem->GetBottom() ) pNewBoxItem->SetLine( NULL, BOX_LINE_BOTTOM ); + if ( pNewBoxItem->GetLeft() ) pNewBoxItem->SetLine( NULL, BOX_LINE_LEFT ); + if ( pNewBoxItem->GetRight() ) pNewBoxItem->SetLine( NULL, BOX_LINE_RIGHT ); + } + if( pNewTLBRItem && pNewTLBRItem->GetLine() ) + pNewTLBRItem->SetLine( 0 ); + if( pNewBLTRItem && pNewBLTRItem->GetLine() ) + pNewBLTRItem->SetLine( 0 ); + } + else + { + if ( bColorOnly ) + { + Color aColor( pLine->GetColor() ); + if( pNewBoxItem ) + { + SET_LINECOLOR( pNewBoxItem->GetTop(), aColor ); + SET_LINECOLOR( pNewBoxItem->GetBottom(), aColor ); + SET_LINECOLOR( pNewBoxItem->GetLeft(), aColor ); + SET_LINECOLOR( pNewBoxItem->GetRight(), aColor ); + } + if( pNewTLBRItem ) + SET_LINECOLOR( pNewTLBRItem->GetLine(), aColor ); + if( pNewBLTRItem ) + SET_LINECOLOR( pNewBLTRItem->GetLine(), aColor ); + } + else + { + if( pNewBoxItem ) + { + SET_LINE( pNewBoxItem->GetTop(), pLine ); + SET_LINE( pNewBoxItem->GetBottom(), pLine ); + SET_LINE( pNewBoxItem->GetLeft(), pLine ); + SET_LINE( pNewBoxItem->GetRight(), pLine ); + } + if( pNewTLBRItem ) + SET_LINE( pNewTLBRItem->GetLine(), pLine ); + if( pNewBLTRItem ) + SET_LINE( pNewBLTRItem->GetLine(), pLine ); + } + } + if( pNewBoxItem ) rNewSet.Put( *pNewBoxItem ); + if( pNewTLBRItem ) rNewSet.Put( *pNewTLBRItem ); + if( pNewBLTRItem ) rNewSet.Put( *pNewBLTRItem ); + + nStart = pData[nPos].nRow + 1; + + if ( nY1 < nStartRow || nY2 > nEndRow ) + { + if (nY1 < nStartRow) nY1=nStartRow; + if (nY2 > nEndRow) nY2=nEndRow; + SetPatternArea( nY1, nY2, pNewPattern, TRUE ); + Search( nStart, nPos ); + } + else + { + //! aus Pool loeschen? + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + + if (Concat(nPos)) + Search(nStart, nPos); + else + nPos++; + } + delete pNewBoxItem; + delete pNewTLBRItem; + delete pNewBLTRItem; + delete pNewPattern; + } + else + { + nStart = pData[nPos].nRow + 1; + nPos++; + } + } + while ((nStart <= nEndRow) && (nPos < nCount)); + } +} + +#undef SET_LINECOLOR +#undef SET_LINE + + +void ScAttrArray::ApplyCacheArea( SCROW nStartRow, SCROW nEndRow, SfxItemPoolCache* pCache ) +{ +#ifdef DBG_UTIL + TestData(); +#endif + + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + DBG_ERROR("Search-Fehler"); + return; + } + + ScAddress aAdrStart( nCol, 0, nTab ); + ScAddress aAdrEnd ( nCol, 0, nTab ); + + do + { + const ScPatternAttr* pOldPattern = pData[nPos].pPattern; + const ScPatternAttr* pNewPattern = (const ScPatternAttr*) &pCache->ApplyTo( *pOldPattern, TRUE ); + ScDocumentPool::CheckRef( *pOldPattern ); + ScDocumentPool::CheckRef( *pNewPattern ); + if (pNewPattern != pOldPattern) + { + SCROW nY1 = nStart; + SCROW nY2 = pData[nPos].nRow; + nStart = pData[nPos].nRow + 1; + + if ( nY1 < nStartRow || nY2 > nEndRow ) + { + if (nY1 < nStartRow) nY1=nStartRow; + if (nY2 > nEndRow) nY2=nEndRow; + SetPatternArea( nY1, nY2, pNewPattern ); + Search( nStart, nPos ); + } + else + { + // ueberpruefen, ob Attributierung die Textbreite der Zelle aendert + + const SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + + BOOL bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rNewSet, rOldSet ) ) + { + aAdrStart.SetRow( nPos ? pData[nPos-1].nRow+1 : 0 ); + aAdrEnd .SetRow( pData[nPos].nRow ); + pDocument->InvalidateTextWidth( &aAdrStart, &aAdrEnd, bNumFormatChanged ); +#ifdef DBG_INVALIDATE + DBGOUTPUT("ApplyCacheArea"); +#endif + } + + // bedingte Formate neu gesetzt oder geloescht ? + + if ( &rNewSet.Get(ATTR_CONDITIONAL) != &rOldSet.Get(ATTR_CONDITIONAL) ) + { + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rOldSet.Get(ATTR_CONDITIONAL)).GetValue() ); + pDocument->ConditionalChanged( ((const SfxUInt32Item&) + rNewSet.Get(ATTR_CONDITIONAL)).GetValue() ); + } + + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pData[nPos].pPattern = pNewPattern; + if (Concat(nPos)) + Search(nStart, nPos); + else + ++nPos; + } + } + else + { +//!!!!!!!!!!!!!!!!!! mit diesem Remove gibt es Abstuerze (Calc1 Import) +//! pDocument->GetPool()->Remove(*pNewPattern); + nStart = pData[nPos].nRow + 1; + ++nPos; + } + } + while (nStart <= nEndRow); + + if (pDocument->IsStreamValid(nTab)) + pDocument->SetStreamValid(nTab, FALSE); + } + +#ifdef DBG_UTIL + TestData(); +#endif +} + + +void lcl_MergeDeep( SfxItemSet& rMergeSet, const SfxItemSet& rSource ) +{ + const SfxPoolItem* pNewItem; + const SfxPoolItem* pOldItem; + for (USHORT nId=ATTR_PATTERN_START; nId<=ATTR_PATTERN_END; nId++) + { + // pMergeSet hat keinen Parent + SfxItemState eOldState = rMergeSet.GetItemState( nId, FALSE, &pOldItem ); + + if ( eOldState == SFX_ITEM_DEFAULT ) // Default + { + SfxItemState eNewState = rSource.GetItemState( nId, TRUE, &pNewItem ); + if ( eNewState == SFX_ITEM_SET ) + { + if ( *pNewItem != rMergeSet.GetPool()->GetDefaultItem(nId) ) + rMergeSet.InvalidateItem( nId ); + } + } + else if ( eOldState == SFX_ITEM_SET ) // Item gesetzt + { + SfxItemState eNewState = rSource.GetItemState( nId, TRUE, &pNewItem ); + if ( eNewState == SFX_ITEM_SET ) + { + if ( pNewItem != pOldItem ) // beide gepuhlt + rMergeSet.InvalidateItem( nId ); + } + else // Default + { + if ( *pOldItem != rSource.GetPool()->GetDefaultItem(nId) ) + rMergeSet.InvalidateItem( nId ); + } + } + // Dontcare bleibt Dontcare + } +} + + +void ScAttrArray::MergePatternArea( SCROW nStartRow, SCROW nEndRow, + ScMergePatternState& rState, BOOL bDeep ) const +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + SCSIZE nPos; + SCROW nStart=0; + if (!Search( nStartRow, nPos )) + { + DBG_ERROR("Search-Fehler"); + return; + } + + do + { + // gleiche Patterns muessen nicht mehrfach angesehen werden + + const ScPatternAttr* pPattern = pData[nPos].pPattern; + if ( pPattern != rState.pOld1 && pPattern != rState.pOld2 ) + { + const SfxItemSet& rThisSet = pPattern->GetItemSet(); + if (rState.pItemSet) + { + // (*ppSet)->MergeValues( rThisSet, FALSE ); + // geht nicht, weil die Vorlagen nicht beruecksichtigt werden + + if (bDeep) + lcl_MergeDeep( *rState.pItemSet, rThisSet ); + else + rState.pItemSet->MergeValues( rThisSet, FALSE ); + } + else + { + // erstes Pattern - in Set ohne Parent kopieren + rState.pItemSet = new SfxItemSet( *rThisSet.GetPool(), rThisSet.GetRanges() ); + rState.pItemSet->Set( rThisSet, bDeep ); + } + + rState.pOld2 = rState.pOld1; + rState.pOld1 = pPattern; + } + + nStart = pData[nPos].nRow + 1; + ++nPos; + } + while (nStart <= nEndRow); + } +} + + + +// Umrandung zusammenbauen + +BOOL lcl_TestAttr( const SvxBorderLine* pOldLine, const SvxBorderLine* pNewLine, + BYTE& rModified, const SvxBorderLine*& rpNew ) +{ + if (rModified == SC_LINE_DONTCARE) + return FALSE; // weiter geht's nicht + + if (rModified == SC_LINE_EMPTY) + { + rModified = SC_LINE_SET; + rpNew = pNewLine; + return TRUE; // zum ersten mal gesetzt + } + + if (pOldLine == pNewLine) + { + rpNew = pOldLine; + return FALSE; + } + + if (pOldLine && pNewLine) + if (*pOldLine == *pNewLine) + { + rpNew = pOldLine; + return FALSE; + } + + rModified = SC_LINE_DONTCARE; + rpNew = NULL; + return TRUE; // andere Linie -> dontcare +} + + +void lcl_MergeToFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, + ScLineFlags& rFlags, const ScPatternAttr* pPattern, + BOOL bLeft, SCCOL nDistRight, BOOL bTop, SCROW nDistBottom ) +{ + // rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst: + const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE); + if ( rMerge.GetColMerge() == nDistRight + 1 ) + nDistRight = 0; + if ( rMerge.GetRowMerge() == nDistBottom + 1 ) + nDistBottom = 0; + + const SvxBoxItem* pCellFrame = (SvxBoxItem*) &pPattern->GetItemSet().Get( ATTR_BORDER ); + const SvxBorderLine* pLeftAttr = pCellFrame->GetLeft(); + const SvxBorderLine* pRightAttr = pCellFrame->GetRight(); + const SvxBorderLine* pTopAttr = pCellFrame->GetTop(); + const SvxBorderLine* pBottomAttr = pCellFrame->GetBottom(); + const SvxBorderLine* pNew; + + if (bTop) + { + if (lcl_TestAttr( pLineOuter->GetTop(), pTopAttr, rFlags.nTop, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_TOP ); + } + else + { + if (lcl_TestAttr( pLineInner->GetHori(), pTopAttr, rFlags.nHori, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_HORI ); + } + + if (nDistBottom == 0) + { + if (lcl_TestAttr( pLineOuter->GetBottom(), pBottomAttr, rFlags.nBottom, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_BOTTOM ); + } + else + { + if (lcl_TestAttr( pLineInner->GetHori(), pBottomAttr, rFlags.nHori, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_HORI ); + } + + if (bLeft) + { + if (lcl_TestAttr( pLineOuter->GetLeft(), pLeftAttr, rFlags.nLeft, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_LEFT ); + } + else + { + if (lcl_TestAttr( pLineInner->GetVert(), pLeftAttr, rFlags.nVert, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_VERT ); + } + + if (nDistRight == 0) + { + if (lcl_TestAttr( pLineOuter->GetRight(), pRightAttr, rFlags.nRight, pNew )) + pLineOuter->SetLine( pNew, BOX_LINE_RIGHT ); + } + else + { + if (lcl_TestAttr( pLineInner->GetVert(), pRightAttr, rFlags.nVert, pNew )) + pLineInner->SetLine( pNew, BOXINFO_LINE_VERT ); + } +} + + +void ScAttrArray::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, + ScLineFlags& rFlags, + SCROW nStartRow, SCROW nEndRow, BOOL bLeft, SCCOL nDistRight ) const +{ + const ScPatternAttr* pPattern; + + if (nStartRow == nEndRow) + { + pPattern = GetPattern( nStartRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, TRUE, 0 ); + } + else + { + pPattern = GetPattern( nStartRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, TRUE, + nEndRow-nStartRow ); + + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nStartRow+1, nStartIndex ); + Search( nEndRow-1, nEndIndex ); + for (SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pPattern = (ScPatternAttr*) pData[i].pPattern; + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, FALSE, + nEndRow - Min( pData[i].nRow, (SCROW)(nEndRow-1) ) ); + // nDistBottom hier immer > 0 + } + + pPattern = GetPattern( nEndRow ); + lcl_MergeToFrame( pLineOuter, pLineInner, rFlags, pPattern, bLeft, nDistRight, FALSE, 0 ); + } +} + +// +// Rahmen anwenden +// + +// ApplyFrame - auf einen Eintrag im Array + + +BOOL ScAttrArray::ApplyFrame( const SvxBoxItem* pBoxItem, + const SvxBoxInfoItem* pBoxInfoItem, + SCROW nStartRow, SCROW nEndRow, + BOOL bLeft, SCCOL nDistRight, BOOL bTop, SCROW nDistBottom ) +{ + DBG_ASSERT( pBoxItem && pBoxInfoItem, "Linienattribute fehlen!" ); + + const ScPatternAttr* pPattern = GetPattern( nStartRow ); + const SvxBoxItem* pOldFrame = (const SvxBoxItem*) + &pPattern->GetItemSet().Get( ATTR_BORDER ); + + // rechten/unteren Rahmen setzen, wenn Zelle bis zum Ende zusammengefasst: + const ScMergeAttr& rMerge = (const ScMergeAttr&)pPattern->GetItem(ATTR_MERGE); + if ( rMerge.GetColMerge() == nDistRight + 1 ) + nDistRight = 0; + if ( rMerge.GetRowMerge() == nDistBottom + 1 ) + nDistBottom = 0; + + SvxBoxItem aNewFrame( *pOldFrame ); + + if ( bLeft ? pBoxInfoItem->IsValid(VALID_LEFT) : pBoxInfoItem->IsValid(VALID_VERT) ) + aNewFrame.SetLine( bLeft ? pBoxItem->GetLeft() : pBoxInfoItem->GetVert(), + BOX_LINE_LEFT ); + if ( (nDistRight==0) ? pBoxInfoItem->IsValid(VALID_RIGHT) : pBoxInfoItem->IsValid(VALID_VERT) ) + aNewFrame.SetLine( (nDistRight==0) ? pBoxItem->GetRight() : pBoxInfoItem->GetVert(), + BOX_LINE_RIGHT ); + if ( bTop ? pBoxInfoItem->IsValid(VALID_TOP) : pBoxInfoItem->IsValid(VALID_HORI) ) + aNewFrame.SetLine( bTop ? pBoxItem->GetTop() : pBoxInfoItem->GetHori(), + BOX_LINE_TOP ); + if ( (nDistBottom==0) ? pBoxInfoItem->IsValid(VALID_BOTTOM) : pBoxInfoItem->IsValid(VALID_HORI) ) + aNewFrame.SetLine( (nDistBottom==0) ? pBoxItem->GetBottom() : pBoxInfoItem->GetHori(), + BOX_LINE_BOTTOM ); + + if (aNewFrame == *pOldFrame) + { + // nothing to do + return FALSE; + } + else + { + SfxItemPoolCache aCache( pDocument->GetPool(), &aNewFrame ); + ApplyCacheArea( nStartRow, nEndRow, &aCache ); + +/* ScPatternAttr* pNewPattern = (ScPatternAttr*) pPattern->Clone(); + pNewPattern->GetItemSet().Put( aNewFrame ); + SetPatternArea( nStartRow, nEndRow, pNewPattern, TRUE ); +*/ + return TRUE; + } +} + + +void ScAttrArray::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner, + SCROW nStartRow, SCROW nEndRow, BOOL bLeft, SCCOL nDistRight ) +{ + if (nStartRow == nEndRow) + ApplyFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight, TRUE, 0 ); + else + { + ApplyFrame( pLineOuter, pLineInner, nStartRow, nStartRow, bLeft, nDistRight, + TRUE, nEndRow-nStartRow ); + + if ( nEndRow > nStartRow+1 ) // innerer Teil vorhanden? + { + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nStartRow+1, nStartIndex ); + Search( nEndRow-1, nEndIndex ); + SCROW nTmpStart = nStartRow+1; + SCROW nTmpEnd; + for (SCSIZE i=nStartIndex; i<=nEndIndex;) + { + nTmpEnd = Min( (SCROW)(nEndRow-1), (SCROW)(pData[i].nRow) ); + BOOL bChanged = ApplyFrame( pLineOuter, pLineInner, nTmpStart, nTmpEnd, + bLeft, nDistRight, FALSE, nEndRow-nTmpEnd ); + nTmpStart = nTmpEnd+1; + if (bChanged) + { + Search(nTmpStart, i); + Search(nEndRow-1, nEndIndex); + } + else + i++; + } + } + + ApplyFrame( pLineOuter, pLineInner, nEndRow, nEndRow, bLeft, nDistRight, FALSE, 0 ); + } +} + + +long lcl_LineSize( const SvxBorderLine& rLine ) +{ + // nur eine Linie -> halbe Breite, min. 20 + // doppelte Linie -> halber Abstand + eine Linie (je min. 20) + + long nTotal = 0; + USHORT nWidth = Max( rLine.GetOutWidth(), rLine.GetInWidth() ); + USHORT nDist = rLine.GetDistance(); + if (nDist) + { + DBG_ASSERT( rLine.GetOutWidth() && rLine.GetInWidth(), + "Linie hat Abstand, aber nur eine Breite ???" ); + +// nTotal += ( nDist > 40 ) ? ( nDist / 2 ) : 20; + nTotal += ( nDist > 20 ) ? nDist : 20; + nTotal += ( nWidth > 20 ) ? nWidth : 20; + } + else if (nWidth) +// nTotal += ( nWidth > 40 ) ? ( nWidth / 2 ) : 20; + nTotal += ( nWidth > 20 ) ? nWidth : 20; + + //! auch halbieren ??? + + return nTotal; +} + + +BOOL ScAttrArray::HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes, + BOOL bLeft, BOOL bRight ) const +{ + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nRow1, nStartIndex ); + Search( nRow2, nEndIndex ); + BOOL bFound = FALSE; + + const SvxBoxItem* pItem = 0; + const SvxBorderLine* pLine = 0; + long nCmp; + + // oben + + pItem = (const SvxBoxItem*) &pData[nStartIndex].pPattern->GetItem(ATTR_BORDER); + pLine = pItem->GetTop(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Top() ) + rSizes.Top() = nCmp; + bFound = TRUE; + } + + // unten + + if ( nEndIndex != nStartIndex ) + pItem = (const SvxBoxItem*) &pData[nEndIndex].pPattern->GetItem(ATTR_BORDER); + pLine = pItem->GetBottom(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Bottom() ) + rSizes.Bottom() = nCmp; + bFound = TRUE; + } + + if ( bLeft || bRight ) + for ( SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pItem = (const SvxBoxItem*) &pData[i].pPattern->GetItem(ATTR_BORDER); + + // links + + if (bLeft) + { + pLine = pItem->GetLeft(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Left() ) + rSizes.Left() = nCmp; + bFound = TRUE; + } + } + + // rechts + + if (bRight) + { + pLine = pItem->GetRight(); + if (pLine) + { + nCmp = lcl_LineSize(*pLine); + if ( nCmp > rSizes.Right() ) + rSizes.Right() = nCmp; + bFound = TRUE; + } + } + } + + return bFound; +} + +// Testen, ob Bereich bestimmtes Attribut enthaelt + +BOOL ScAttrArray::HasAttrib( SCROW nRow1, SCROW nRow2, USHORT nMask ) const +{ + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nRow1, nStartIndex ); + Search( nRow2, nEndIndex ); + BOOL bFound = FALSE; + + for (SCSIZE i=nStartIndex; i<=nEndIndex && !bFound; i++) + { + const ScPatternAttr* pPattern = pData[i].pPattern; + if ( nMask & HASATTR_MERGED ) + { + const ScMergeAttr* pMerge = + (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + if ( pMerge->GetColMerge() > 1 || pMerge->GetRowMerge() > 1 ) + bFound = TRUE; + } + if ( nMask & ( HASATTR_OVERLAPPED | HASATTR_NOTOVERLAPPED | HASATTR_AUTOFILTER ) ) + { + const ScMergeFlagAttr* pMergeFlag = + (const ScMergeFlagAttr*) &pPattern->GetItem( ATTR_MERGE_FLAG ); + if ( (nMask & HASATTR_OVERLAPPED) && pMergeFlag->IsOverlapped() ) + bFound = TRUE; + if ( (nMask & HASATTR_NOTOVERLAPPED) && !pMergeFlag->IsOverlapped() ) + bFound = TRUE; + if ( (nMask & HASATTR_AUTOFILTER) && pMergeFlag->HasAutoFilter() ) + bFound = TRUE; + } + if ( nMask & HASATTR_LINES ) + { + const SvxBoxItem* pBox = + (const SvxBoxItem*) &pPattern->GetItem( ATTR_BORDER ); + if ( pBox->GetLeft() || pBox->GetRight() || pBox->GetTop() || pBox->GetBottom() ) + bFound = TRUE; + } + if ( nMask & HASATTR_SHADOW ) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + if ( pShadow->GetLocation() != SVX_SHADOW_NONE ) + bFound = TRUE; + } + if ( nMask & HASATTR_CONDITIONAL ) + { + const SfxUInt32Item* pConditional = + (const SfxUInt32Item*) &pPattern->GetItem( ATTR_CONDITIONAL ); + if ( pConditional->GetValue() != 0 ) + bFound = TRUE; + } + if ( nMask & HASATTR_PROTECTED ) + { + const ScProtectionAttr* pProtect = + (const ScProtectionAttr*) &pPattern->GetItem( ATTR_PROTECTION ); + if ( pProtect->GetProtection() || pProtect->GetHideCell() ) + bFound = TRUE; + } + if ( nMask & HASATTR_ROTATE ) + { + const SfxInt32Item* pRotate = + (const SfxInt32Item*) &pPattern->GetItem( ATTR_ROTATE_VALUE ); + // 90 or 270 degrees is former SvxOrientationItem - only look for other values + // (see ScPatternAttr::GetCellOrientation) + INT32 nAngle = pRotate->GetValue(); + if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 ) + bFound = TRUE; + } + if ( nMask & HASATTR_NEEDHEIGHT ) + { + if (pPattern->GetCellOrientation() != SVX_ORIENTATION_STANDARD) + bFound = TRUE; + else if (((const SfxBoolItem&)pPattern->GetItem( ATTR_LINEBREAK )).GetValue()) + bFound = TRUE; + else if ((SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern-> + GetItem( ATTR_HOR_JUSTIFY )).GetValue() == SVX_HOR_JUSTIFY_BLOCK) + bFound = TRUE; + else if (((const SfxUInt32Item&)pPattern->GetItem( ATTR_CONDITIONAL )).GetValue()) + bFound = TRUE; + else if (((const SfxInt32Item&)pPattern->GetItem( ATTR_ROTATE_VALUE )).GetValue()) + bFound = TRUE; + } + if ( nMask & ( HASATTR_SHADOW_RIGHT | HASATTR_SHADOW_DOWN ) ) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + SvxShadowLocation eLoc = pShadow->GetLocation(); + if ( nMask & HASATTR_SHADOW_RIGHT ) + if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + bFound = TRUE; + if ( nMask & HASATTR_SHADOW_DOWN ) + if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + bFound = TRUE; + } + if ( nMask & HASATTR_RTL ) + { + const SvxFrameDirectionItem& rDirection = + (const SvxFrameDirectionItem&) pPattern->GetItem( ATTR_WRITINGDIR ); + if ( rDirection.GetValue() == FRMDIR_HORI_RIGHT_TOP ) + bFound = TRUE; + } + if ( nMask & HASATTR_RIGHTORCENTER ) + { + // called only if the sheet is LTR, so physical=logical alignment can be assumed + SvxCellHorJustify eHorJust = (SvxCellHorJustify) + ((const SvxHorJustifyItem&) pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue(); + if ( eHorJust == SVX_HOR_JUSTIFY_RIGHT || eHorJust == SVX_HOR_JUSTIFY_CENTER ) + bFound = TRUE; + } + } + + return bFound; +} + +// Bereich um evtl. enthaltene Zusammenfassungen erweitern +// und evtl. MergeFlag anpassen (bRefresh) + +BOOL ScAttrArray::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow, + SCCOL& rPaintCol, SCROW& rPaintRow, + BOOL bRefresh, BOOL bAttrs ) +{ + const ScPatternAttr* pPattern; + const ScMergeAttr* pItem; + SCSIZE nStartIndex; + SCSIZE nEndIndex; + Search( nStartRow, nStartIndex ); + Search( nEndRow, nEndIndex ); + BOOL bFound = FALSE; + + for (SCSIZE i=nStartIndex; i<=nEndIndex; i++) + { + pPattern = pData[i].pPattern; + pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + SCsCOL nCountX = pItem->GetColMerge(); + SCsROW nCountY = pItem->GetRowMerge(); + if (nCountX>1 || nCountY>1) + { + SCROW nThisRow = (i>0) ? pData[i-1].nRow+1 : 0; + SCCOL nMergeEndCol = nThisCol + nCountX - 1; + SCROW nMergeEndRow = nThisRow + nCountY - 1; + if (nMergeEndCol > rPaintCol && nMergeEndCol <= MAXCOL) + rPaintCol = nMergeEndCol; + if (nMergeEndRow > rPaintRow && nMergeEndRow <= MAXROW) + rPaintRow = nMergeEndRow; + bFound = TRUE; + + if (bAttrs) + { + const SvxShadowItem* pShadow = + (const SvxShadowItem*) &pPattern->GetItem( ATTR_SHADOW ); + SvxShadowLocation eLoc = pShadow->GetLocation(); + if ( eLoc == SVX_SHADOW_TOPRIGHT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + if ( nMergeEndCol+1 > rPaintCol && nMergeEndCol < MAXCOL ) + rPaintCol = nMergeEndCol+1; + if ( eLoc == SVX_SHADOW_BOTTOMLEFT || eLoc == SVX_SHADOW_BOTTOMRIGHT ) + if ( nMergeEndRow+1 > rPaintRow && nMergeEndRow < MAXROW ) + rPaintRow = nMergeEndRow+1; + } + + if (bRefresh) + { + if ( nMergeEndCol > nThisCol ) + pDocument->ApplyFlagsTab( nThisCol+1, nThisRow, nMergeEndCol, pData[i].nRow, + nTab, SC_MF_HOR ); + if ( nMergeEndRow > nThisRow ) + pDocument->ApplyFlagsTab( nThisCol, nThisRow+1, nThisCol, nMergeEndRow, + nTab, SC_MF_VER ); + if ( nMergeEndCol > nThisCol && nMergeEndRow > nThisRow ) + pDocument->ApplyFlagsTab( nThisCol+1, nThisRow+1, nMergeEndCol, nMergeEndRow, + nTab, SC_MF_HOR | SC_MF_VER ); + + Search( nThisRow, i ); // Daten wurden veraendert + Search( nStartRow, nStartIndex ); + Search( nEndRow, nEndIndex ); + } + } + } + + return bFound; +} + + +BOOL ScAttrArray::RemoveAreaMerge(SCROW nStartRow, SCROW nEndRow) +{ + BOOL bFound = FALSE; + const ScPatternAttr* pPattern; + const ScMergeAttr* pItem; + SCSIZE nIndex; + + Search( nStartRow, nIndex ); + SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisStart < nStartRow) + nThisStart = nStartRow; + + while ( nThisStart <= nEndRow ) + { + SCROW nThisEnd = pData[nIndex].nRow; + if (nThisEnd > nEndRow) + nThisEnd = nEndRow; + + pPattern = pData[nIndex].pPattern; + pItem = (const ScMergeAttr*) &pPattern->GetItem( ATTR_MERGE ); + SCsCOL nCountX = pItem->GetColMerge(); + SCsROW nCountY = pItem->GetRowMerge(); + if (nCountX>1 || nCountY>1) + { + const ScMergeAttr* pAttr = (const ScMergeAttr*) + &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); + const ScMergeFlagAttr* pFlagAttr = (const ScMergeFlagAttr*) + &pDocument->GetPool()->GetDefaultItem( ATTR_MERGE_FLAG ); + + DBG_ASSERT( nCountY==1 || nThisStart==nThisEnd, "was'n hier los?" ); + + SCCOL nThisCol = nCol; + SCCOL nMergeEndCol = nThisCol + nCountX - 1; + SCROW nMergeEndRow = nThisEnd + nCountY - 1; + + //! ApplyAttr fuer Bereiche !!! + + for (SCROW nThisRow = nThisStart; nThisRow <= nThisEnd; nThisRow++) + pDocument->ApplyAttr( nThisCol, nThisRow, nTab, *pAttr ); + + ScPatternAttr* pNewPattern = new ScPatternAttr( pDocument->GetPool() ); + SfxItemSet* pSet = &pNewPattern->GetItemSet(); + pSet->Put( *pFlagAttr ); + pDocument->ApplyPatternAreaTab( nThisCol, nThisStart, nMergeEndCol, nMergeEndRow, + nTab, *pNewPattern ); + delete pNewPattern; + + Search( nThisEnd, nIndex ); // Daten wurden veraendert !!! + } + + ++nIndex; + if ( nIndex < nCount ) + nThisStart = pData[nIndex-1].nRow+1; + else + nThisStart = MAXROW+1; // Ende + } + + return bFound; +} + + // Bereich loeschen, aber Merge-Flags stehenlassen + +void ScAttrArray::DeleteAreaSafe(SCROW nStartRow, SCROW nEndRow) +{ + SetPatternAreaSafe( nStartRow, nEndRow, pDocument->GetDefPattern(), TRUE ); +} + + +void ScAttrArray::SetPatternAreaSafe( SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr* pWantedPattern, BOOL bDefault ) +{ + const ScPatternAttr* pOldPattern; + const ScMergeFlagAttr* pItem; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + BOOL bFirstUse = TRUE; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + if (pOldPattern != pWantedPattern) //! else-Zweig ? + { + if (nThisRow < nStartRow) nThisRow = nStartRow; + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + pItem = (const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ); + + if (pItem->IsOverlapped() || pItem->HasAutoFilter()) + { + // #108045# default-constructing a ScPatternAttr for DeleteArea doesn't work + // because it would have no cell style information. + // Instead, the document's GetDefPattern is copied. Since it is passed as + // pWantedPattern, no special treatment of default is needed here anymore. + ScPatternAttr* pNewPattern = new ScPatternAttr( *pWantedPattern ); + SfxItemSet* pSet = &pNewPattern->GetItemSet(); + pSet->Put( *pItem ); + SetPatternArea( nThisRow, nAttrRow, pNewPattern, TRUE ); + delete pNewPattern; + } + else + { + if ( !bDefault ) + { + if (bFirstUse) + bFirstUse = FALSE; + else + pDocument->GetPool()->Put( *pWantedPattern ); // im Pool ist es schon! + } + SetPatternArea( nThisRow, nAttrRow, pWantedPattern ); + } + + Search( nThisRow, nIndex ); // Daten wurden veraendert !!! + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } +} + + +BOOL ScAttrArray::ApplyFlags( SCROW nStartRow, SCROW nEndRow, INT16 nFlags ) +{ + const ScPatternAttr* pOldPattern; + + INT16 nOldValue; + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + BOOL bChanged = FALSE; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue(); + if ( (nOldValue | nFlags) != nOldValue ) + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue | nFlags ) ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, TRUE ); + Search( nThisRow, nIndex ); // Daten wurden veraendert !!! + bChanged = TRUE; + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } + + return bChanged; +} + + +BOOL ScAttrArray::RemoveFlags( SCROW nStartRow, SCROW nEndRow, INT16 nFlags ) +{ + const ScPatternAttr* pOldPattern; + + INT16 nOldValue; + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + BOOL bChanged = FALSE; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + nOldValue = ((const ScMergeFlagAttr*) &pOldPattern->GetItem( ATTR_MERGE_FLAG ))->GetValue(); + if ( (nOldValue & ~nFlags) != nOldValue ) + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( ScMergeFlagAttr( nOldValue & ~nFlags ) ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, TRUE ); + Search( nThisRow, nIndex ); // Daten wurden veraendert !!! + bChanged = TRUE; + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } + + return bChanged; +} + + +void ScAttrArray::ClearItems( SCROW nStartRow, SCROW nEndRow, const USHORT* pWhich ) +{ + const ScPatternAttr* pOldPattern; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + if ( pOldPattern->HasItemsSet( pWhich ) ) + { + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.ClearItems( pWhich ); + + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, TRUE ); + Search( nThisRow, nIndex ); // Daten wurden veraendert !!! + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } +} + + +void ScAttrArray::ChangeIndent( SCROW nStartRow, SCROW nEndRow, BOOL bIncrement ) +{ + SCSIZE nIndex; + Search( nStartRow, nIndex ); + SCROW nThisStart = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisStart < nStartRow) nThisStart = nStartRow; + + while ( nThisStart <= nEndRow ) + { + const ScPatternAttr* pOldPattern = pData[nIndex].pPattern; + const SfxItemSet& rOldSet = pOldPattern->GetItemSet(); + const SfxPoolItem* pItem; + + BOOL bNeedJust = ( rOldSet.GetItemState( ATTR_HOR_JUSTIFY, FALSE, &pItem ) != SFX_ITEM_SET + || ((const SvxHorJustifyItem*)pItem)->GetValue() != SVX_HOR_JUSTIFY_LEFT ); + USHORT nOldValue = ((const SfxUInt16Item&)rOldSet.Get( ATTR_INDENT )).GetValue(); + USHORT nNewValue = nOldValue; + if ( bIncrement ) + { + if ( nNewValue < SC_MAX_INDENT ) + { + nNewValue += SC_INDENT_STEP; + if ( nNewValue > SC_MAX_INDENT ) nNewValue = SC_MAX_INDENT; + } + } + else + { + if ( nNewValue > 0 ) + { + if ( nNewValue > SC_INDENT_STEP ) + nNewValue -= SC_INDENT_STEP; + else + nNewValue = 0; + } + } + + if ( bNeedJust || nNewValue != nOldValue ) + { + SCROW nThisEnd = pData[nIndex].nRow; + SCROW nAttrRow = Min( nThisEnd, nEndRow ); + ScPatternAttr aNewPattern(*pOldPattern); + aNewPattern.GetItemSet().Put( SfxUInt16Item( ATTR_INDENT, nNewValue ) ); + if ( bNeedJust ) + aNewPattern.GetItemSet().Put( + SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) ); + SetPatternArea( nThisStart, nAttrRow, &aNewPattern, TRUE ); + + nThisStart = nThisEnd + 1; + Search( nThisStart, nIndex ); // Daten wurden veraendert !!! + } + else + { + nThisStart = pData[nIndex].nRow + 1; // weiterzaehlen... + ++nIndex; + } + } +} + + +SCsROW ScAttrArray::GetNextUnprotected( SCsROW nRow, BOOL bUp ) const +{ + long nRet = nRow; + if (VALIDROW(nRow)) + { + SCSIZE nIndex; + Search(nRow, nIndex); + while (((const ScProtectionAttr&)pData[nIndex].pPattern-> + GetItem(ATTR_PROTECTION)).GetProtection()) + { + if (bUp) + { + if (nIndex==0) + return -1; // nichts gefunden + --nIndex; + nRet = pData[nIndex].nRow; + } + else + { + nRet = pData[nIndex].nRow+1; + ++nIndex; + if (nIndex>=nCount) + return MAXROW+1; // nichts gefunden + } + } + } + return nRet; +} + + +void ScAttrArray::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, BOOL* pUsed, BOOL bReset ) +{ + SCROW nStart = 0; + SCSIZE nPos = 0; + while (nPos < nCount) + { + SCROW nEnd = pData[nPos].nRow; + if (pData[nPos].pPattern->GetStyleSheet() == pStyleSheet) + { +// for (SCROW nRow = nStart; nRow <= nEnd; nRow++) +// pUsed[nRow] = TRUE; + + memset( &pUsed[nStart], TRUE, nEnd-nStart+1 ); + + if (bReset) + { + ScPatternAttr* pNewPattern = new ScPatternAttr(*pData[nPos].pPattern); + pDocument->GetPool()->Remove(*pData[nPos].pPattern); + pNewPattern->SetStyleSheet( (ScStyleSheet*) + pDocument->GetStyleSheetPool()-> + Find( ScGlobal::GetRscString(STR_STYLENAME_STANDARD), + SFX_STYLE_FAMILY_PARA, + SFXSTYLEBIT_AUTO | SCSTYLEBIT_STANDARD ) ); + pData[nPos].pPattern = (const ScPatternAttr*) + &pDocument->GetPool()->Put(*pNewPattern); + delete pNewPattern; + + if (Concat(nPos)) + { + Search(nStart, nPos); + --nPos; // wegen ++ am Ende + } + } + } + nStart = nEnd + 1; + ++nPos; + } +} + + +BOOL ScAttrArray::IsStyleSheetUsed( const ScStyleSheet& rStyle, + BOOL bGatherAllStyles ) const +{ + BOOL bIsUsed = FALSE; + SCSIZE nPos = 0; + + while ( nPos < nCount ) + { + const ScStyleSheet* pStyle = pData[nPos].pPattern->GetStyleSheet(); + if ( pStyle ) + { + pStyle->SetUsage( ScStyleSheet::USED ); + if ( pStyle == &rStyle ) + { + if ( !bGatherAllStyles ) + return TRUE; + bIsUsed = TRUE; + } + } + nPos++; + } + + return bIsUsed; +} + + +BOOL ScAttrArray::IsEmpty() const +{ + if (nCount == 1) + { + if ( pData[0].pPattern != pDocument->GetDefPattern() ) + return FALSE; + else + return TRUE; + } + else + return FALSE; +} + + +//UNUSED2008-05 SCROW ScAttrArray::GetFirstEntryPos() const +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( nCount, "nCount = 0" ); +//UNUSED2008-05 +//UNUSED2008-05 if ( pData[0].pPattern != pDocument->GetDefPattern() ) +//UNUSED2008-05 return 0; +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 if (nCount==1) +//UNUSED2008-05 return 0; // leer +//UNUSED2008-05 else +//UNUSED2008-05 return pData[0].nRow + 1; +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 SCROW ScAttrArray::GetLastEntryPos( BOOL bIncludeBottom ) const +//UNUSED2008-05 { +//UNUSED2008-05 DBG_ASSERT( nCount, "nCount == 0" ); +//UNUSED2008-05 +//UNUSED2008-05 if (bIncludeBottom) +//UNUSED2008-05 bIncludeBottom = ( pData[nCount-1].pPattern != pDocument->GetDefPattern() ); +//UNUSED2008-05 +//UNUSED2008-05 if (bIncludeBottom) +//UNUSED2008-05 return MAXROW; +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 if (nCount<=1) +//UNUSED2008-05 return 0; // leer +//UNUSED2008-05 else +//UNUSED2008-05 return pData[nCount-2].nRow; +//UNUSED2008-05 } +//UNUSED2008-05 } + + +BOOL ScAttrArray::GetFirstVisibleAttr( SCROW& rFirstRow ) const +{ + DBG_ASSERT( nCount, "nCount == 0" ); + + BOOL bFound = FALSE; + SCSIZE nStart = 0; + + // Skip first entry if more than 1 row. + // Entries at the end are not skipped, GetFirstVisibleAttr may be larger than GetLastVisibleAttr. + + SCSIZE nVisStart = 1; + while ( nVisStart < nCount && pData[nVisStart].pPattern->IsVisibleEqual(*pData[nVisStart-1].pPattern) ) + ++nVisStart; + if ( nVisStart >= nCount || pData[nVisStart-1].nRow > 0 ) // more than 1 row? + nStart = nVisStart; + + while ( nStart < nCount && !bFound ) + { + if ( pData[nStart].pPattern->IsVisible() ) + { + rFirstRow = nStart ? ( pData[nStart-1].nRow + 1 ) : 0; + bFound = TRUE; + } + else + ++nStart; + } + + return bFound; +} + +// size (rows) of a range of attributes after cell content where the search is stopped +// (more than a default page size, 2*42 because it's as good as any number) + +const SCROW SC_VISATTR_STOP = 84; + +BOOL ScAttrArray::GetLastVisibleAttr( SCROW& rLastRow, SCROW nLastData ) const +{ + // #i30830# changed behavior: + // ignore all attributes starting with the first run of SC_VISATTR_STOP equal rows + // below the last content cell + + if ( nLastData == MAXROW ) + { + rLastRow = MAXROW; // can't look for attributes below MAXROW + return TRUE; + } + + BOOL bFound = FALSE; + + // loop backwards from the end instead of using Search, assuming that + // there usually aren't many attributes below the last cell + + SCSIZE nPos = nCount; + while ( nPos > 0 && pData[nPos-1].nRow > nLastData ) + { + SCSIZE nEndPos = nPos - 1; + SCSIZE nStartPos = nEndPos; // find range of visually equal formats + while ( nStartPos > 0 && + pData[nStartPos-1].nRow > nLastData && + pData[nStartPos-1].pPattern->IsVisibleEqual(*pData[nStartPos].pPattern) ) + --nStartPos; + + SCROW nAttrStartRow = ( nStartPos > 0 ) ? ( pData[nStartPos-1].nRow + 1 ) : 0; + if ( nAttrStartRow <= nLastData ) + nAttrStartRow = nLastData + 1; + SCROW nAttrSize = pData[nEndPos].nRow + 1 - nAttrStartRow; + if ( nAttrSize >= SC_VISATTR_STOP ) + { + bFound = FALSE; // ignore this range and below + } + else if ( !bFound && pData[nEndPos].pPattern->IsVisible() ) + { + rLastRow = pData[nEndPos].nRow; + bFound = TRUE; + } + + nPos = nStartPos; // look further from the top of the range + } + + return bFound; +} + + +BOOL ScAttrArray::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const +{ + SCSIZE nIndex; + Search( nStartRow, nIndex ); + SCROW nThisStart = nStartRow; + BOOL bFound = FALSE; + while ( nIndex < nCount && nThisStart <= nEndRow && !bFound ) + { + if ( pData[nIndex].pPattern->IsVisible() ) + bFound = TRUE; + + nThisStart = pData[nIndex].nRow + 1; + ++nIndex; + } + + return bFound; +} + + +BOOL ScAttrArray::IsVisibleEqual( const ScAttrArray& rOther, + SCROW nStartRow, SCROW nEndRow ) const +{ + BOOL bEqual = TRUE; + SCSIZE nThisPos = 0; + SCSIZE nOtherPos = 0; + if ( nStartRow > 0 ) + { + Search( nStartRow, nThisPos ); + rOther.Search( nStartRow, nOtherPos ); + } + + while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual ) + { + SCROW nThisRow = pData[nThisPos].nRow; + SCROW nOtherRow = rOther.pData[nOtherPos].nRow; + const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern; + const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern; + bEqual = ( pThisPattern == pOtherPattern || + pThisPattern->IsVisibleEqual(*pOtherPattern) ); + + if ( nThisRow >= nOtherRow ) + { + if ( nOtherRow >= nEndRow ) break; + ++nOtherPos; + } + if ( nThisRow <= nOtherRow ) + { + if ( nThisRow >= nEndRow ) break; + ++nThisPos; + } + } + + return bEqual; +} + + +BOOL ScAttrArray::IsAllEqual( const ScAttrArray& rOther, SCROW nStartRow, SCROW nEndRow ) const +{ + //! mit IsVisibleEqual zusammenfassen? + + BOOL bEqual = TRUE; + SCSIZE nThisPos = 0; + SCSIZE nOtherPos = 0; + if ( nStartRow > 0 ) + { + Search( nStartRow, nThisPos ); + rOther.Search( nStartRow, nOtherPos ); + } + + while ( nThisPos<nCount && nOtherPos<rOther.nCount && bEqual ) + { + SCROW nThisRow = pData[nThisPos].nRow; + SCROW nOtherRow = rOther.pData[nOtherPos].nRow; + const ScPatternAttr* pThisPattern = pData[nThisPos].pPattern; + const ScPatternAttr* pOtherPattern = rOther.pData[nOtherPos].pPattern; + bEqual = ( pThisPattern == pOtherPattern ); + + if ( nThisRow >= nOtherRow ) + { + if ( nOtherRow >= nEndRow ) break; + ++nOtherPos; + } + if ( nThisRow <= nOtherRow ) + { + if ( nThisRow >= nEndRow ) break; + ++nThisPos; + } + } + + return bEqual; +} + + +BOOL ScAttrArray::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const +{ + // horizontal zusammengefasste duerfen nicht herausgeschoben werden + // (ob die ganze Zusammenfassung betroffen ist, ist hier nicht zu erkennen) + + BOOL bTest = TRUE; + if (!IsEmpty()) + { + SCSIZE nIndex = 0; + if ( nStartRow > 0 ) + Search( nStartRow, nIndex ); + + for ( ; nIndex < nCount; nIndex++ ) + { + if ( ((const ScMergeFlagAttr&)pData[nIndex].pPattern-> + GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped() ) + { + bTest = FALSE; // darf nicht herausgeschoben werden + break; + } + if ( pData[nIndex].nRow >= nEndRow ) // Ende des Bereichs + break; + } + } + return bTest; +} + + +BOOL ScAttrArray::TestInsertRow( SCSIZE nSize ) const +{ + // wenn die erste herausgeschobene Zeile vertikal ueberlappt ist, + // wuerde eine kaputte Zusammenfassung uebrigbleiben + + if (pData) + { + // MAXROW + 1 - nSize = erste herausgeschobene Zeile + + SCSIZE nFirstLost = nCount-1; + while ( nFirstLost && pData[nFirstLost-1].nRow >= sal::static_int_cast<SCROW>(MAXROW + 1 - nSize) ) + --nFirstLost; + + if ( ((const ScMergeFlagAttr&)pData[nFirstLost].pPattern-> + GetItem(ATTR_MERGE_FLAG)).IsVerOverlapped() ) + return FALSE; + } + + return TRUE; +} + + +void ScAttrArray::InsertRow( SCROW nStartRow, SCSIZE nSize ) +{ + if (!pData) + return; + + SCROW nSearch = nStartRow > 0 ? nStartRow - 1 : 0; // Vorgaenger erweitern + SCSIZE nIndex; + Search( nSearch, nIndex ); + + // ein gesetztes ScMergeAttr darf nicht ausgedehnt werden + // (darum hinterher wieder loeschen) + + BOOL bDoMerge = ((const ScMergeAttr&) pData[nIndex].pPattern->GetItem(ATTR_MERGE)).IsMerged(); + + SCSIZE nRemove = 0; + SCSIZE i; + for (i = nIndex; i < nCount-1; i++) + { + SCROW nNew = pData[i].nRow + nSize; + if ( nNew >= MAXROW ) // Ende erreicht ? + { + nNew = MAXROW; + if (!nRemove) + nRemove = i+1; // folgende loeschen + } + pData[i].nRow = nNew; + } + + // muessen Eintraege am Ende geloescht werden? + + if (nRemove && nRemove < nCount) + DeleteRange( nRemove, nCount-1 ); + + if (bDoMerge) // ausgedehntes ScMergeAttr wieder reparieren + { + //! ApplyAttr fuer Bereiche !!! + + const SfxPoolItem& rDef = pDocument->GetPool()->GetDefaultItem( ATTR_MERGE ); + for (SCSIZE nAdd=0; nAdd<nSize; nAdd++) + pDocument->ApplyAttr( nCol, nStartRow+nAdd, nTab, rDef ); + + // im eingefuegten Bereich ist nichts zusammengefasst + } + + // Don't duplicate the merge flags in the inserted row. + RemoveFlags( nStartRow, nStartRow+nSize-1, SC_MF_ALL ); +} + + +void ScAttrArray::DeleteRow( SCROW nStartRow, SCSIZE nSize ) +{ + if (pData) + { + BOOL bFirst=TRUE; + SCSIZE nStartIndex = 0; + SCSIZE nEndIndex = 0; + SCSIZE i; + + for ( i = 0; i < nCount-1; i++) + if (pData[i].nRow >= nStartRow && pData[i].nRow <= sal::static_int_cast<SCROW>(nStartRow+nSize-1)) + { + if (bFirst) + { + nStartIndex = i; + bFirst = FALSE; + } + nEndIndex = i; + } + if (!bFirst) + { + SCROW nStart; + if (nStartIndex==0) + nStart = 0; + else + nStart = pData[nStartIndex-1].nRow + 1; + + if (nStart < nStartRow) + { + pData[nStartIndex].nRow = nStartRow - 1; + ++nStartIndex; + } + if (nEndIndex >= nStartIndex) + { + DeleteRange( nStartIndex, nEndIndex ); + if (nStartIndex > 0) + if ( pData[nStartIndex-1].pPattern == pData[nStartIndex].pPattern ) + DeleteRange( nStartIndex-1, nStartIndex-1 ); + } + } + for (i = 0; i < nCount-1; i++) + if (pData[i].nRow >= nStartRow) + pData[i].nRow -= nSize; + +// unten nicht Default-Pattern nachschieben, um Druckbereiche erkennen zu koennen +// stattdessen nur Merge-Flags loeschen + + RemoveFlags( MAXROW-nSize+1, MAXROW, SC_MF_HOR | SC_MF_VER | SC_MF_AUTO ); + } +} + + +void ScAttrArray::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex ) +{ + ScDocumentPool* pDocPool = pDocument->GetPool(); + for (SCSIZE i = nStartIndex; i <= nEndIndex; i++) + pDocPool->Remove(*pData[i].pPattern); + + memmove( &pData[nStartIndex], &pData[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ScAttrEntry) ); + nCount -= nEndIndex-nStartIndex+1; +} + + +void ScAttrArray::DeleteArea(SCROW nStartRow, SCROW nEndRow) +{ + RemoveAreaMerge( nStartRow, nEndRow ); // von zusammengefassten auch die Flags loeschen + + if ( !HasAttrib( nStartRow, nEndRow, HASATTR_OVERLAPPED | HASATTR_AUTOFILTER) ) + SetPatternArea( nStartRow, nEndRow, pDocument->GetDefPattern() ); + else + DeleteAreaSafe( nStartRow, nEndRow ); // Merge-Flags stehenlassen +} + + +void ScAttrArray::DeleteHardAttr(SCROW nStartRow, SCROW nEndRow) +{ + const ScPatternAttr* pDefPattern = pDocument->GetDefPattern(); + const ScPatternAttr* pOldPattern; + + SCSIZE nIndex; + SCROW nRow; + SCROW nThisRow; + + Search( nStartRow, nIndex ); + nThisRow = (nIndex>0) ? pData[nIndex-1].nRow+1 : 0; + if (nThisRow < nStartRow) nThisRow = nStartRow; + + while ( nThisRow <= nEndRow ) + { + pOldPattern = pData[nIndex].pPattern; + + if ( pOldPattern->GetItemSet().Count() ) // harte Attribute ? + { + nRow = pData[nIndex].nRow; + SCROW nAttrRow = Min( (SCROW)nRow, (SCROW)nEndRow ); + + ScPatternAttr aNewPattern(*pOldPattern); + SfxItemSet& rSet = aNewPattern.GetItemSet(); + for (USHORT nId = ATTR_PATTERN_START; nId <= ATTR_PATTERN_END; nId++) + if (nId != ATTR_MERGE && nId != ATTR_MERGE_FLAG) + rSet.ClearItem(nId); + + if ( aNewPattern == *pDefPattern ) + SetPatternArea( nThisRow, nAttrRow, pDefPattern, FALSE ); + else + SetPatternArea( nThisRow, nAttrRow, &aNewPattern, TRUE ); + + Search( nThisRow, nIndex ); // Daten wurden veraendert !!! + } + + ++nIndex; + nThisRow = pData[nIndex-1].nRow+1; + } +} + + // Verschieben innerhalb eines Dokuments + +void ScAttrArray::MoveTo(SCROW nStartRow, SCROW nEndRow, ScAttrArray& rAttrArray) +{ + SCROW nStart = nStartRow; + for (SCSIZE i = 0; i < nCount; i++) + { + if ((pData[i].nRow >= nStartRow) && ((i==0) ? TRUE : pData[i-1].nRow < nEndRow)) + { + // Kopieren (bPutToPool=TRUE) + rAttrArray.SetPatternArea( nStart, Min( (SCROW)pData[i].nRow, (SCROW)nEndRow ), + pData[i].pPattern, TRUE ); + } + nStart = Max( (SCROW)nStart, (SCROW)(pData[i].nRow + 1) ); + } + DeleteArea(nStartRow, nEndRow); +} + + + // Kopieren zwischen Dokumenten (Clipboard) + +void ScAttrArray::CopyArea( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray, + INT16 nStripFlags ) +{ + nStartRow -= nDy; // Source + nEndRow -= nDy; + + SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0); + SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW); + + ScDocumentPool* pSourceDocPool = pDocument->GetPool(); + ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool(); + BOOL bSamePool = (pSourceDocPool==pDestDocPool); + + for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++) + { + if (pData[i].nRow >= nStartRow) + { + const ScPatternAttr* pOldPattern = pData[i].pPattern; + const ScPatternAttr* pNewPattern; + + if (IsDefaultItem( pOldPattern )) + { + // am Default muss nichts veraendert werden + + pNewPattern = (const ScPatternAttr*) + &pDestDocPool->GetDefaultItem( ATTR_PATTERN ); + } + else if ( nStripFlags ) + { + ScPatternAttr* pTmpPattern = new ScPatternAttr( *pOldPattern ); + INT16 nNewFlags = 0; + if ( nStripFlags != SC_MF_ALL ) + nNewFlags = ((const ScMergeFlagAttr&)pTmpPattern->GetItem(ATTR_MERGE_FLAG)). + GetValue() & ~nStripFlags; + + if ( nNewFlags ) + pTmpPattern->GetItemSet().Put( ScMergeFlagAttr( nNewFlags ) ); + else + pTmpPattern->GetItemSet().ClearItem( ATTR_MERGE_FLAG ); + + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pTmpPattern); + else + pNewPattern = pTmpPattern->PutInPool( rAttrArray.pDocument, pDocument ); + delete pTmpPattern; + } + else + { + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern); + else + pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); + } + + rAttrArray.SetPatternArea(nDestStart, + Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern); + } + + // when pasting from clipboard and skipping filtered rows, the adjusted end position + // can be negative + nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1)); + } +} + + // Flags stehenlassen + //! mit CopyArea zusammenfassen !!! + +void ScAttrArray::CopyAreaSafe( SCROW nStartRow, SCROW nEndRow, long nDy, ScAttrArray& rAttrArray ) +{ + nStartRow -= nDy; // Source + nEndRow -= nDy; + + SCROW nDestStart = Max((long)((long)nStartRow + nDy), (long) 0); + SCROW nDestEnd = Min((long)((long)nEndRow + nDy), (long) MAXROW); + + if ( !rAttrArray.HasAttrib( nDestStart, nDestEnd, HASATTR_OVERLAPPED ) ) + { + CopyArea( nStartRow+nDy, nEndRow+nDy, nDy, rAttrArray ); + return; + } + + ScDocumentPool* pSourceDocPool = pDocument->GetPool(); + ScDocumentPool* pDestDocPool = rAttrArray.pDocument->GetPool(); + BOOL bSamePool = (pSourceDocPool==pDestDocPool); + + for (SCSIZE i = 0; (i < nCount) && (nDestStart <= nDestEnd); i++) + { + if (pData[i].nRow >= nStartRow) + { + const ScPatternAttr* pOldPattern = pData[i].pPattern; + const ScPatternAttr* pNewPattern; + + if (bSamePool) + pNewPattern = (ScPatternAttr*) &pDestDocPool->Put(*pOldPattern); + else + pNewPattern = pOldPattern->PutInPool( rAttrArray.pDocument, pDocument ); + + rAttrArray.SetPatternAreaSafe(nDestStart, + Min((SCROW)(pData[i].nRow + nDy), nDestEnd), pNewPattern, FALSE); + } + + // when pasting from clipboard and skipping filtered rows, the adjusted end position + // can be negative + nDestStart = Max((long)nDestStart, (long)(pData[i].nRow + nDy + 1)); + } +} + + +SCsROW ScAttrArray::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle, + BOOL bUp, ScMarkArray* pMarkArray ) +{ + BOOL bFound = FALSE; + + if (pMarkArray) + { + nRow = pMarkArray->GetNextMarked( nRow, bUp ); + if (!VALIDROW(nRow)) + return nRow; + } + + SCSIZE nIndex; + Search(nRow, nIndex); + const ScPatternAttr* pPattern = pData[nIndex].pPattern; + + while (nIndex < nCount && !bFound) + { + if (pPattern->GetStyleSheet() == pSearchStyle) + { + if (pMarkArray) + { + nRow = pMarkArray->GetNextMarked( nRow, bUp ); + SCROW nStart = nIndex ? pData[nIndex-1].nRow+1 : 0; + if (nRow >= nStart && nRow <= pData[nIndex].nRow) + bFound = TRUE; + } + else + bFound = TRUE; + } + + if (!bFound) + { + if (bUp) + { + if (nIndex==0) + { + nIndex = nCount; + nRow = -1; + } + else + { + --nIndex; + nRow = pData[nIndex].nRow; + pPattern = pData[nIndex].pPattern; + } + } + else + { + nRow = pData[nIndex].nRow+1; + ++nIndex; + if (nIndex<nCount) + pPattern = pData[nIndex].pPattern; + } + } + } + + DBG_ASSERT( bFound || !ValidRow(nRow), "interner Fehler in ScAttrArray::SearchStyle" ); + + return nRow; +} + + +BOOL ScAttrArray::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow, + const ScStyleSheet* pSearchStyle, BOOL bUp, ScMarkArray* pMarkArray ) +{ + SCsROW nStartRow = SearchStyle( rRow, pSearchStyle, bUp, pMarkArray ); + if (VALIDROW(nStartRow)) + { + SCSIZE nIndex; + Search(nStartRow,nIndex); + + rRow = nStartRow; + if (bUp) + { + if (nIndex>0) + rEndRow = pData[nIndex-1].nRow + 1; + else + rEndRow = 0; + if (pMarkArray) + { + SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, TRUE ); + if (nMarkEnd>rEndRow) + rEndRow = nMarkEnd; + } + } + else + { + rEndRow = pData[nIndex].nRow; + if (pMarkArray) + { + SCROW nMarkEnd = pMarkArray->GetMarkEnd( nStartRow, FALSE ); + if (nMarkEnd<rEndRow) + rEndRow = nMarkEnd; + } + } + + return TRUE; + } + else + return FALSE; +} + +//------------------------------------------------------------------------ +// +// Laden / Speichern +// + + +#if 0 +void ScAttrArray::Save( SvStream& /* rStream */ ) const +{ +#if SC_ROWLIMIT_STREAM_ACCESS +#error address types changed! + ScWriteHeader aHdr( rStream, 8 ); + + ScDocumentPool* pDocPool = pDocument->GetPool(); + + USHORT nSaveCount = nCount; + SCROW nSaveMaxRow = pDocument->GetSrcMaxRow(); + if ( nSaveMaxRow != MAXROW ) + { + if ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow ) + { + pDocument->SetLostData(); // Warnung ausgeben + do + --nSaveCount; + while ( nSaveCount > 1 && pData[nSaveCount-2].nRow >= nSaveMaxRow ); + } + } + + rStream << nSaveCount; + + const SfxPoolItem* pItem; + for (SCSIZE i=0; i<nSaveCount; i++) + { + rStream << Min( pData[i].nRow, nSaveMaxRow ); + + const ScPatternAttr* pPattern = pData[i].pPattern; + pDocPool->StoreSurrogate( rStream, pPattern ); + + // FALSE, weil ATTR_CONDITIONAL (noch) nicht in Vorlagen: + if (pPattern->GetItemSet().GetItemState(ATTR_CONDITIONAL,FALSE,&pItem) == SFX_ITEM_SET) + pDocument->SetConditionalUsed( ((const SfxUInt32Item*)pItem)->GetValue() ); + + if (pPattern->GetItemSet().GetItemState(ATTR_VALIDDATA,FALSE,&pItem) == SFX_ITEM_SET) + pDocument->SetValidationUsed( ((const SfxUInt32Item*)pItem)->GetValue() ); + } +#endif // SC_ROWLIMIT_STREAM_ACCESS +} + + +void ScAttrArray::Load( SvStream& /* rStream */ ) +{ +#if SC_ROWLIMIT_STREAM_ACCESS +#error address types changed! + ScDocumentPool* pDocPool = pDocument->GetPool(); + + ScReadHeader aHdr( rStream ); + + USHORT nNewCount; + rStream >> nNewCount; + if ( nNewCount > MAXROW+1 ) // wuerde das Array zu gross? + { + pDocument->SetLostData(); + rStream.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return; + } + + Reset( pDocument->GetDefPattern(), FALSE ); // loeschen + pData = new ScAttrEntry[nNewCount]; // neu anlegen + for (SCSIZE i=0; i<nNewCount; i++) + { + rStream >> pData[i].nRow; + + USHORT nWhich = ATTR_PATTERN; + const ScPatternAttr* pNewPattern = (const ScPatternAttr*) + pDocPool->LoadSurrogate( rStream, nWhich, ATTR_PATTERN ); + if (!pNewPattern) + { + // da is was schiefgelaufen + DBG_ERROR("ScAttrArray::Load: Surrogat nicht im Pool"); + pNewPattern = pDocument->GetDefPattern(); + } + ScDocumentPool::CheckRef( *pNewPattern ); + pData[i].pPattern = pNewPattern; + + // LoadSurrogate erhoeht auch die Ref + } + nCount = nLimit = nNewCount; + + if ( nCount > 1 && pData[nCount-2].nRow >= MAXROW ) // faengt ein Attribut hinter MAXROW an? + { + pDocument->SetLostData(); + rStream.SetError( SVSTREAM_FILEFORMAT_ERROR ); + return; + } + + if ( pDocument->GetSrcMaxRow() != MAXROW ) // Ende anpassen? + { + // Ende immer auf MAXROW umsetzen (nur auf 32 Bit) + + DBG_ASSERT( pData[nCount-1].nRow == pDocument->GetSrcMaxRow(), "Attribut-Ende ?!?" ); + pData[nCount-1].nRow = MAXROW; + } +#endif // SC_ROWLIMIT_STREAM_ACCESS +} +#endif + + +//UNUSED2008-05 void ScAttrArray::ConvertFontsAfterLoad() +//UNUSED2008-05 { +//UNUSED2008-05 ScFontToSubsFontConverter_AutoPtr xFontConverter; +//UNUSED2008-05 const ULONG nFlags = FONTTOSUBSFONT_IMPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS; +//UNUSED2008-05 SCSIZE nIndex = 0; +//UNUSED2008-05 SCROW nThisRow = 0; +//UNUSED2008-05 +//UNUSED2008-05 while ( nThisRow <= MAXROW ) +//UNUSED2008-05 { +//UNUSED2008-05 const ScPatternAttr* pOldPattern = pData[nIndex].pPattern; +//UNUSED2008-05 const SfxPoolItem* pItem; +//UNUSED2008-05 if( pOldPattern->GetItemSet().GetItemState( ATTR_FONT, FALSE, &pItem ) == SFX_ITEM_SET ) +//UNUSED2008-05 { +//UNUSED2008-05 const SvxFontItem* pFontItem = (const SvxFontItem*) pItem; +//UNUSED2008-05 const String& rOldName = pFontItem->GetFamilyName(); +//UNUSED2008-05 xFontConverter = CreateFontToSubsFontConverter( rOldName, nFlags ); +//UNUSED2008-05 if ( xFontConverter ) +//UNUSED2008-05 { +//UNUSED2008-05 String aNewName( GetFontToSubsFontName( xFontConverter ) ); +//UNUSED2008-05 if ( aNewName != rOldName ) +//UNUSED2008-05 { +//UNUSED2008-05 SCROW nAttrRow = pData[nIndex].nRow; +//UNUSED2008-05 SvxFontItem aNewItem( pFontItem->GetFamily(), aNewName, +//UNUSED2008-05 pFontItem->GetStyleName(), pFontItem->GetPitch(), +//UNUSED2008-05 RTL_TEXTENCODING_DONTKNOW, ATTR_FONT ); +//UNUSED2008-05 ScPatternAttr aNewPattern( *pOldPattern ); +//UNUSED2008-05 aNewPattern.GetItemSet().Put( aNewItem ); +//UNUSED2008-05 SetPatternArea( nThisRow, nAttrRow, &aNewPattern, TRUE ); +//UNUSED2008-05 Search( nThisRow, nIndex ); //! data changed +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 ++nIndex; +//UNUSED2008-05 nThisRow = pData[nIndex-1].nRow+1; +//UNUSED2008-05 } +//UNUSED2008-05 } + diff --git a/sc/source/core/data/attrib.cxx b/sc/source/core/data/attrib.cxx new file mode 100644 index 000000000000..dcc6418445fa --- /dev/null +++ b/sc/source/core/data/attrib.cxx @@ -0,0 +1,1331 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: attrib.cxx,v $ + * $Revision: 1.19.32.4 $ + * + * 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 <com/sun/star/util/CellProtection.hpp> +#include <com/sun/star/util/XProtectable.hpp> +#include <com/sun/star/text/XText.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <svx/boxitem.hxx> +#include <svx/editdata.hxx> +#include <svx/editeng.hxx> +#include <svx/editobj.hxx> +#include <svx/flditem.hxx> + +#include "attrib.hxx" +#include "global.hxx" +#include "editutil.hxx" +#include "sc.hrc" +#include "globstr.hrc" + +#include "textuno.hxx" // ScHeaderFooterContentObj + +using namespace com::sun::star; + +//------------------------------------------------------------------------ + +TYPEINIT1(ScMergeAttr, SfxPoolItem); +TYPEINIT1_AUTOFACTORY(ScProtectionAttr, SfxPoolItem); +TYPEINIT1(ScRangeItem, SfxPoolItem); +TYPEINIT1(ScTableListItem, SfxPoolItem); +TYPEINIT1(ScPageHFItem, SfxPoolItem); +TYPEINIT1(ScViewObjectModeItem, SfxEnumItem); +TYPEINIT1(ScDoubleItem, SfxPoolItem); +TYPEINIT1(ScPageScaleToItem, SfxPoolItem); + +//------------------------------------------------------------------------ + +// +// allgemeine Hilfsfunktionen +// + +BOOL ScHasPriority( const SvxBorderLine* pThis, const SvxBorderLine* pOther ) +{ +// DBG_ASSERT( pThis || pOther, "LineAttr == 0" ); + + if (!pThis) + return FALSE; + if (!pOther) + return TRUE; + + USHORT nThisSize = pThis->GetOutWidth() + pThis->GetDistance() + pThis->GetInWidth(); + USHORT nOtherSize = pOther->GetOutWidth() + pOther->GetDistance() + pOther->GetInWidth(); + + if (nThisSize > nOtherSize) + return TRUE; + else if (nThisSize < nOtherSize) + return FALSE; + else + { + if ( pOther->GetInWidth() && !pThis->GetInWidth() ) + return TRUE; + else if ( pThis->GetInWidth() && !pOther->GetInWidth() ) + return FALSE; + else + { + return TRUE; //! ??? + } + } +} + + +// +// Item - Implementierungen +// + +//------------------------------------------------------------------------ +// Merge +//------------------------------------------------------------------------ + +ScMergeAttr::ScMergeAttr(): + SfxPoolItem(ATTR_MERGE), + nColMerge(0), + nRowMerge(0) +{} + +//------------------------------------------------------------------------ + +ScMergeAttr::ScMergeAttr( SCsCOL nCol, SCsROW nRow): + SfxPoolItem(ATTR_MERGE), + nColMerge(nCol), + nRowMerge(nRow) +{} + +//------------------------------------------------------------------------ + +ScMergeAttr::ScMergeAttr(const ScMergeAttr& rItem): + SfxPoolItem(ATTR_MERGE) +{ + nColMerge = rItem.nColMerge; + nRowMerge = rItem.nRowMerge; +} + +ScMergeAttr::~ScMergeAttr() +{ +} + +//------------------------------------------------------------------------ + +String ScMergeAttr::GetValueText() const +{ + String aString( '(' ); + aString += String::CreateFromInt32( nColMerge ); + aString += ','; + aString += String::CreateFromInt32( nRowMerge ); + aString += ')'; + return aString; +} + +//------------------------------------------------------------------------ + +int ScMergeAttr::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( Which() != rItem.Which() || Type() == rItem.Type(), "which ==, type !=" ); + return (Which() == rItem.Which()) + && (nColMerge == ((ScMergeAttr&)rItem).nColMerge) + && (nRowMerge == ((ScMergeAttr&)rItem).nRowMerge); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScMergeAttr::Clone( SfxItemPool * ) const +{ + return new ScMergeAttr(*this); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScMergeAttr::Create( SvStream& rStream, USHORT /* nVer */ ) const +{ + INT16 nCol; + INT16 nRow; + rStream >> nCol; + rStream >> nRow; + return new ScMergeAttr(static_cast<SCCOL>(nCol),static_cast<SCROW>(nRow)); +} + +//------------------------------------------------------------------------ +// MergeFlag +//------------------------------------------------------------------------ + +ScMergeFlagAttr::ScMergeFlagAttr(): + SfxInt16Item(ATTR_MERGE_FLAG, 0) +{ +} + +//------------------------------------------------------------------------ + +ScMergeFlagAttr::ScMergeFlagAttr(INT16 nFlags): + SfxInt16Item(ATTR_MERGE_FLAG, nFlags) +{ +} + +ScMergeFlagAttr::~ScMergeFlagAttr() +{ +} + +//------------------------------------------------------------------------ +// Protection +//------------------------------------------------------------------------ + +ScProtectionAttr::ScProtectionAttr(): + SfxPoolItem(ATTR_PROTECTION), + bProtection(TRUE), + bHideFormula(FALSE), + bHideCell(FALSE), + bHidePrint(FALSE) +{ +} + +//------------------------------------------------------------------------ + +ScProtectionAttr::ScProtectionAttr( BOOL bProtect, BOOL bHFormula, + BOOL bHCell, BOOL bHPrint): + SfxPoolItem(ATTR_PROTECTION), + bProtection(bProtect), + bHideFormula(bHFormula), + bHideCell(bHCell), + bHidePrint(bHPrint) +{ +} + +//------------------------------------------------------------------------ + +ScProtectionAttr::ScProtectionAttr(const ScProtectionAttr& rItem): + SfxPoolItem(ATTR_PROTECTION) +{ + bProtection = rItem.bProtection; + bHideFormula = rItem.bHideFormula; + bHideCell = rItem.bHideCell; + bHidePrint = rItem.bHidePrint; +} + +ScProtectionAttr::~ScProtectionAttr() +{ +} + +//------------------------------------------------------------------------ + +BOOL ScProtectionAttr::QueryValue( uno::Any& rVal, BYTE nMemberId ) const +{ + nMemberId &= ~CONVERT_TWIPS; + switch ( nMemberId ) + { + case 0 : + { + util::CellProtection aProtection; + aProtection.IsLocked = bProtection; + aProtection.IsFormulaHidden = bHideFormula; + aProtection.IsHidden = bHideCell; + aProtection.IsPrintHidden = bHidePrint; + rVal <<= aProtection; + break; + } + case MID_1 : + rVal <<= (sal_Bool ) bProtection; break; + case MID_2 : + rVal <<= (sal_Bool ) bHideFormula; break; + case MID_3 : + rVal <<= (sal_Bool ) bHideCell; break; + case MID_4 : + rVal <<= (sal_Bool ) bHidePrint; break; + default: + DBG_ERROR("Wrong MemberID!"); + return FALSE; + } + + return TRUE; +} + +BOOL ScProtectionAttr::PutValue( const uno::Any& rVal, BYTE nMemberId ) +{ + BOOL bRet = FALSE; + sal_Bool bVal = sal_Bool(); + nMemberId &= ~CONVERT_TWIPS; + switch ( nMemberId ) + { + case 0 : + { + util::CellProtection aProtection; + if ( rVal >>= aProtection ) + { + bProtection = aProtection.IsLocked; + bHideFormula = aProtection.IsFormulaHidden; + bHideCell = aProtection.IsHidden; + bHidePrint = aProtection.IsPrintHidden; + bRet = TRUE; + } + else + { + DBG_ERROR("exception - wrong argument"); + } + break; + } + case MID_1 : + bRet = (rVal >>= bVal); if (bRet) bProtection=bVal; break; + case MID_2 : + bRet = (rVal >>= bVal); if (bRet) bHideFormula=bVal; break; + case MID_3 : + bRet = (rVal >>= bVal); if (bRet) bHideCell=bVal; break; + case MID_4 : + bRet = (rVal >>= bVal); if (bRet) bHidePrint=bVal; break; + default: + DBG_ERROR("Wrong MemberID!"); + } + + return bRet; +} + +//------------------------------------------------------------------------ + +String ScProtectionAttr::GetValueText() const +{ + String aValue; + String aStrYes ( ScGlobal::GetRscString(STR_YES) ); + String aStrNo ( ScGlobal::GetRscString(STR_NO) ); + sal_Unicode cDelim = ','; + + aValue = '('; + aValue += (bProtection ? aStrYes : aStrNo); aValue += cDelim; + aValue += (bHideFormula ? aStrYes : aStrNo); aValue += cDelim; + aValue += (bHideCell ? aStrYes : aStrNo); aValue += cDelim; + aValue += (bHidePrint ? aStrYes : aStrNo); + aValue += ')'; + + return aValue; +} + +//------------------------------------------------------------------------ + +SfxItemPresentation ScProtectionAttr::GetPresentation + ( + SfxItemPresentation ePres, + SfxMapUnit /* eCoreMetric */, + SfxMapUnit /* ePresMetric */, + String& rText, + const IntlWrapper* /* pIntl */ + ) const +{ + String aStrYes ( ScGlobal::GetRscString(STR_YES) ); + String aStrNo ( ScGlobal::GetRscString(STR_NO) ); + String aStrSep = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( ": " )); + String aStrDelim = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( ", " )); + + switch ( ePres ) + { + case SFX_ITEM_PRESENTATION_NONE: + rText.Erase(); + break; + + case SFX_ITEM_PRESENTATION_NAMELESS: + rText = GetValueText(); + break; + + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_PROTECTION); rText += aStrSep; + rText += (bProtection ? aStrYes : aStrNo); rText += aStrDelim; + rText += ScGlobal::GetRscString(STR_FORMULAS); rText += aStrSep; + rText += (!bHideFormula ? aStrYes : aStrNo); rText += aStrDelim; + rText += ScGlobal::GetRscString(STR_HIDE); rText += aStrSep; + rText += (bHideCell ? aStrYes : aStrNo); rText += aStrDelim; + rText += ScGlobal::GetRscString(STR_PRINT); rText += aStrSep; + rText += (!bHidePrint ? aStrYes : aStrNo); + break; + + default: + ePres = SFX_ITEM_PRESENTATION_NONE; + } + + return ePres; +} + +//------------------------------------------------------------------------ + +int ScProtectionAttr::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( Which() != rItem.Which() || Type() == rItem.Type(), "which ==, type !=" ); + return (Which() == rItem.Which()) + && (bProtection == ((ScProtectionAttr&)rItem).bProtection) + && (bHideFormula == ((ScProtectionAttr&)rItem).bHideFormula) + && (bHideCell == ((ScProtectionAttr&)rItem).bHideCell) + && (bHidePrint == ((ScProtectionAttr&)rItem).bHidePrint); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScProtectionAttr::Clone( SfxItemPool * ) const +{ + return new ScProtectionAttr(*this); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScProtectionAttr::Create( SvStream& rStream, USHORT /* n */ ) const +{ + BOOL bProtect; + BOOL bHFormula; + BOOL bHCell; + BOOL bHPrint; + + rStream >> bProtect; + rStream >> bHFormula; + rStream >> bHCell; + rStream >> bHPrint; + + return new ScProtectionAttr(bProtect,bHFormula,bHCell,bHPrint); +} + +//------------------------------------------------------------------------ + +BOOL ScProtectionAttr::SetProtection( BOOL bProtect) +{ + bProtection = bProtect; + return TRUE; +} + +//------------------------------------------------------------------------ + +BOOL ScProtectionAttr::SetHideFormula( BOOL bHFormula) +{ + bHideFormula = bHFormula; + return TRUE; +} + +//------------------------------------------------------------------------ + +BOOL ScProtectionAttr::SetHideCell( BOOL bHCell) +{ + bHideCell = bHCell; + return TRUE; +} + +//------------------------------------------------------------------------ + +BOOL ScProtectionAttr::SetHidePrint( BOOL bHPrint) +{ + bHidePrint = bHPrint; + return TRUE; +} + +// ----------------------------------------------------------------------- +// ScRangeItem - Tabellenbereich +// ----------------------------------------------------------------------- + +int ScRangeItem::operator==( const SfxPoolItem& rAttr ) const +{ + DBG_ASSERT( SfxPoolItem::operator==(rAttr), "unequal types" ); + + return ( aRange == ( (ScRangeItem&)rAttr ).aRange ); +} + +// ----------------------------------------------------------------------- + +SfxPoolItem* ScRangeItem::Clone( SfxItemPool* ) const +{ + return new ScRangeItem( *this ); +} + +//------------------------------------------------------------------------ + +SfxItemPresentation ScRangeItem::GetPresentation + ( + SfxItemPresentation ePres, + SfxMapUnit /* eCoreUnit */, + SfxMapUnit /* ePresUnit */, + String& rText, + const IntlWrapper* /* pIntl */ + ) const +{ + rText.Erase(); + + switch ( ePres ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_AREA); + rText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( ": " )); +// break;// Durchfallen !!! + + case SFX_ITEM_PRESENTATION_NAMELESS: + { + String aText; + /* Always use OOo:A1 format */ + aRange.Format( aText ); + rText += aText; + } + break; + + default: + { + // added to avoid warnings + } + } + + return ePres; +} + +// ----------------------------------------------------------------------- +// ScTableListItem - Liste von Tabellen(-nummern) +// ----------------------------------------------------------------------- + +ScTableListItem::ScTableListItem( const ScTableListItem& rCpy ) + : SfxPoolItem ( rCpy.Which() ), + nCount ( rCpy.nCount ) +{ + if ( nCount > 0 ) + { + pTabArr = new SCTAB [nCount]; + + for ( USHORT i=0; i<nCount; i++ ) + pTabArr[i] = rCpy.pTabArr[i]; + } + else + pTabArr = NULL; +} + +// ----------------------------------------------------------------------- + +//UNUSED2008-05 ScTableListItem::ScTableListItem( const USHORT nWhichP, const List& rList ) +//UNUSED2008-05 : SfxPoolItem ( nWhichP ), +//UNUSED2008-05 nCount ( 0 ), +//UNUSED2008-05 pTabArr ( NULL ) +//UNUSED2008-05 { +//UNUSED2008-05 SetTableList( rList ); +//UNUSED2008-05 } + +// ----------------------------------------------------------------------- + +ScTableListItem::~ScTableListItem() +{ + delete [] pTabArr; +} + +// ----------------------------------------------------------------------- + +ScTableListItem& ScTableListItem::operator=( const ScTableListItem& rCpy ) +{ + delete [] pTabArr; + + if ( rCpy.nCount > 0 ) + { + pTabArr = new SCTAB [rCpy.nCount]; + for ( USHORT i=0; i<rCpy.nCount; i++ ) + pTabArr[i] = rCpy.pTabArr[i]; + } + else + pTabArr = NULL; + + nCount = rCpy.nCount; + + return *this; +} + +// ----------------------------------------------------------------------- + +int ScTableListItem::operator==( const SfxPoolItem& rAttr ) const +{ + DBG_ASSERT( SfxPoolItem::operator==(rAttr), "unequal types" ); + + ScTableListItem& rCmp = (ScTableListItem&)rAttr; + BOOL bEqual = (nCount == rCmp.nCount); + + if ( nCount > 0 ) + { + USHORT i=0; + + bEqual = ( pTabArr && rCmp.pTabArr ); + + while ( bEqual && i<nCount ) + { + bEqual = ( pTabArr[i] == rCmp.pTabArr[i] ); + i++; + } + } + return bEqual; +} + +// ----------------------------------------------------------------------- + +SfxPoolItem* ScTableListItem::Clone( SfxItemPool* ) const +{ + return new ScTableListItem( *this ); +} + +//------------------------------------------------------------------------ + +SfxItemPresentation ScTableListItem::GetPresentation + ( + SfxItemPresentation ePres, + SfxMapUnit /* eCoreUnit */, + SfxMapUnit /* ePresUnit */, + String& rText, + const IntlWrapper* /* pIntl */ + ) const +{ + const sal_Unicode cDelim = ','; + + switch ( ePres ) + { + case SFX_ITEM_PRESENTATION_NONE: + rText.Erase(); + return ePres; + + case SFX_ITEM_PRESENTATION_NAMELESS: + { + rText = '('; + if ( nCount>0 && pTabArr ) + for ( USHORT i=0; i<nCount; i++ ) + { + rText += String::CreateFromInt32( pTabArr[i] ); + if ( i<(nCount-1) ) + rText += cDelim; + } + rText += ')'; + } + return ePres; + + case SFX_ITEM_PRESENTATION_COMPLETE: + rText.Erase(); + return SFX_ITEM_PRESENTATION_NONE; + + default: + { + // added to avoid warnings + } + } + + return SFX_ITEM_PRESENTATION_NONE; +} + +// ----------------------------------------------------------------------- + +//UNUSED2009-05 BOOL ScTableListItem::GetTableList( List& aList ) const +//UNUSED2009-05 { +//UNUSED2009-05 for ( USHORT i=0; i<nCount; i++ ) +//UNUSED2009-05 aList.Insert( new SCTAB( pTabArr[i] ) ); +//UNUSED2009-05 +//UNUSED2009-05 return ( nCount > 0 ); +//UNUSED2009-05 } + +// ----------------------------------------------------------------------- + +//UNUSED2009-05 void ScTableListItem::SetTableList( const List& rList ) +//UNUSED2009-05 { +//UNUSED2009-05 nCount = (USHORT)rList.Count(); +//UNUSED2009-05 +//UNUSED2009-05 delete [] pTabArr; +//UNUSED2009-05 +//UNUSED2009-05 if ( nCount > 0 ) +//UNUSED2009-05 { +//UNUSED2009-05 pTabArr = new SCTAB [nCount]; +//UNUSED2009-05 +//UNUSED2009-05 for ( USHORT i=0; i<nCount; i++ ) +//UNUSED2009-05 pTabArr[i] = *( (SCTAB*)rList.GetObject( i ) ); +//UNUSED2009-05 } +//UNUSED2009-05 else +//UNUSED2009-05 pTabArr = NULL; +//UNUSED2009-05 } + + +// ----------------------------------------------------------------------- +// ScPageHFItem - Daten der Kopf-/Fusszeilen +// ----------------------------------------------------------------------- + +ScPageHFItem::ScPageHFItem( USHORT nWhichP ) + : SfxPoolItem ( nWhichP ), + pLeftArea ( NULL ), + pCenterArea ( NULL ), + pRightArea ( NULL ) +{ +} + +//------------------------------------------------------------------------ + +ScPageHFItem::ScPageHFItem( const ScPageHFItem& rItem ) + : SfxPoolItem ( rItem ), + pLeftArea ( NULL ), + pCenterArea ( NULL ), + pRightArea ( NULL ) +{ + if ( rItem.pLeftArea ) + pLeftArea = rItem.pLeftArea->Clone(); + if ( rItem.pCenterArea ) + pCenterArea = rItem.pCenterArea->Clone(); + if ( rItem.pRightArea ) + pRightArea = rItem.pRightArea->Clone(); +} + +//------------------------------------------------------------------------ + +ScPageHFItem::~ScPageHFItem() +{ + delete pLeftArea; + delete pCenterArea; + delete pRightArea; +} + +//------------------------------------------------------------------------ + +BOOL ScPageHFItem::QueryValue( uno::Any& rVal, BYTE /* nMemberId */ ) const +{ + uno::Reference<sheet::XHeaderFooterContent> xContent = + new ScHeaderFooterContentObj( pLeftArea, pCenterArea, pRightArea ); + + rVal <<= xContent; + return TRUE; +} + +BOOL ScPageHFItem::PutValue( const uno::Any& rVal, BYTE /* nMemberId */ ) +{ + BOOL bRet = FALSE; + uno::Reference<sheet::XHeaderFooterContent> xContent; + if ( rVal >>= xContent ) + { + if ( xContent.is() ) + { + ScHeaderFooterContentObj* pImp = + ScHeaderFooterContentObj::getImplementation( xContent ); + if (pImp) + { + const EditTextObject* pImpLeft = pImp->GetLeftEditObject(); + delete pLeftArea; + pLeftArea = pImpLeft ? pImpLeft->Clone() : NULL; + + const EditTextObject* pImpCenter = pImp->GetCenterEditObject(); + delete pCenterArea; + pCenterArea = pImpCenter ? pImpCenter->Clone() : NULL; + + const EditTextObject* pImpRight = pImp->GetRightEditObject(); + delete pRightArea; + pRightArea = pImpRight ? pImpRight->Clone() : NULL; + + if ( !pLeftArea || !pCenterArea || !pRightArea ) + { + // keine Texte auf NULL stehen lassen + ScEditEngineDefaulter aEngine( EditEngine::CreatePool(), TRUE ); + if (!pLeftArea) + pLeftArea = aEngine.CreateTextObject(); + if (!pCenterArea) + pCenterArea = aEngine.CreateTextObject(); + if (!pRightArea) + pRightArea = aEngine.CreateTextObject(); + } + + bRet = TRUE; + } + } + } + + if (!bRet) + { + DBG_ERROR("exception - wrong argument"); + } + + return bRet; +} + +//------------------------------------------------------------------------ + +String ScPageHFItem::GetValueText() const +{ + return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("ScPageHFItem")); +} + +//------------------------------------------------------------------------ + +int ScPageHFItem::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rItem ), "unequal Which or Type" ); + + const ScPageHFItem& r = (const ScPageHFItem&)rItem; + + return ScGlobal::EETextObjEqual(pLeftArea, r.pLeftArea) + && ScGlobal::EETextObjEqual(pCenterArea, r.pCenterArea) + && ScGlobal::EETextObjEqual(pRightArea, r.pRightArea); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScPageHFItem::Clone( SfxItemPool* ) const +{ + return new ScPageHFItem( *this ); +} + +//------------------------------------------------------------------------ + +void lcl_SetSpace( String& rStr, const ESelection& rSel ) +{ + // Text durch ein Leerzeichen ersetzen, damit Positionen stimmen: + + xub_StrLen nLen = rSel.nEndPos-rSel.nStartPos; + rStr.Erase( rSel.nStartPos, nLen-1 ); + rStr.SetChar( rSel.nStartPos, ' ' ); +} + +BOOL lcl_ConvertFields(EditEngine& rEng, const String* pCommands) +{ + BOOL bChange = FALSE; + USHORT nParCnt = rEng.GetParagraphCount(); + for (USHORT nPar = 0; nPar<nParCnt; nPar++) + { + String aStr = rEng.GetText( nPar ); + xub_StrLen nPos; + + while ((nPos = aStr.Search(pCommands[0])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[0].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + while ((nPos = aStr.Search(pCommands[1])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[1].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxPagesField(), EE_FEATURE_FIELD), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + while ((nPos = aStr.Search(pCommands[2])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[2].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxDateField(Date(),SVXDATETYPE_VAR), EE_FEATURE_FIELD), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + while ((nPos = aStr.Search(pCommands[3])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[3].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxTimeField(), EE_FEATURE_FIELD ), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + while ((nPos = aStr.Search(pCommands[4])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[4].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + while ((nPos = aStr.Search(pCommands[5])) != STRING_NOTFOUND) + { + ESelection aSel( nPar,nPos, nPar,nPos+pCommands[5].Len() ); + rEng.QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), aSel ); + lcl_SetSpace(aStr, aSel ); bChange = TRUE; + } + } + return bChange; +} + +#define SC_FIELD_COUNT 6 + +SfxPoolItem* ScPageHFItem::Create( SvStream& rStream, USHORT nVer ) const +{ + EditTextObject* pLeft = EditTextObject::Create(rStream); + EditTextObject* pCenter = EditTextObject::Create(rStream); + EditTextObject* pRight = EditTextObject::Create(rStream); + + DBG_ASSERT( pLeft && pCenter && pRight, "Error reading ScPageHFItem" ); + + if ( pLeft == NULL || pLeft->GetParagraphCount() == 0 || + pCenter == NULL || pCenter->GetParagraphCount() == 0 || + pRight == NULL || pRight->GetParagraphCount() == 0 ) + { + // If successfully loaded, each object contains at least one paragraph. + // Excel import in 5.1 created broken TextObjects (#67442#) that are + // corrected here to avoid saving wrong files again (#90487#). + + ScEditEngineDefaulter aEngine( EditEngine::CreatePool(), TRUE ); + if ( pLeft == NULL || pLeft->GetParagraphCount() == 0 ) + { + delete pLeft; + pLeft = aEngine.CreateTextObject(); + } + if ( pCenter == NULL || pCenter->GetParagraphCount() == 0 ) + { + delete pCenter; + pCenter = aEngine.CreateTextObject(); + } + if ( pRight == NULL || pRight->GetParagraphCount() == 0 ) + { + delete pRight; + pRight = aEngine.CreateTextObject(); + } + } + + if ( nVer < 1 ) // alte Feldbefehle umsetzen + { + USHORT i; + const String& rDel = ScGlobal::GetRscString( STR_HFCMD_DELIMITER ); + String aCommands[SC_FIELD_COUNT]; + for (i=0; i<SC_FIELD_COUNT; i++) + aCommands[i] = rDel; + aCommands[0] += ScGlobal::GetRscString(STR_HFCMD_PAGE); + aCommands[1] += ScGlobal::GetRscString(STR_HFCMD_PAGES); + aCommands[2] += ScGlobal::GetRscString(STR_HFCMD_DATE); + aCommands[3] += ScGlobal::GetRscString(STR_HFCMD_TIME); + aCommands[4] += ScGlobal::GetRscString(STR_HFCMD_FILE); + aCommands[5] += ScGlobal::GetRscString(STR_HFCMD_TABLE); + for (i=0; i<SC_FIELD_COUNT; i++) + aCommands[i] += rDel; + + ScEditEngineDefaulter aEngine( EditEngine::CreatePool(), TRUE ); + aEngine.SetText(*pLeft); + if (lcl_ConvertFields(aEngine,aCommands)) + { + delete pLeft; + pLeft = aEngine.CreateTextObject(); + } + aEngine.SetText(*pCenter); + if (lcl_ConvertFields(aEngine,aCommands)) + { + delete pCenter; + pCenter = aEngine.CreateTextObject(); + } + aEngine.SetText(*pRight); + if (lcl_ConvertFields(aEngine,aCommands)) + { + delete pRight; + pRight = aEngine.CreateTextObject(); + } + } + else if ( nVer < 2 ) + { // nichts tun, SvxFileField nicht gegen SvxExtFileField austauschen + } + + ScPageHFItem* pItem = new ScPageHFItem( Which() ); + pItem->SetArea( pLeft, SC_HF_LEFTAREA ); + pItem->SetArea( pCenter, SC_HF_CENTERAREA ); + pItem->SetArea( pRight, SC_HF_RIGHTAREA ); + + return pItem; +} + +//------------------------------------------------------------------------ + +//UNUSED2009-05 class ScFieldChangerEditEngine : public ScEditEngineDefaulter +//UNUSED2009-05 { +//UNUSED2009-05 TypeId aExtFileId; +//UNUSED2009-05 USHORT nConvPara; +//UNUSED2009-05 xub_StrLen nConvPos; +//UNUSED2009-05 BOOL bConvert; +//UNUSED2009-05 +//UNUSED2009-05 public: +//UNUSED2009-05 ScFieldChangerEditEngine( SfxItemPool* pEnginePool, BOOL bDeleteEnginePool ); +//UNUSED2009-05 virtual ~ScFieldChangerEditEngine() {} +//UNUSED2009-05 +//UNUSED2009-05 virtual String CalcFieldValue( const SvxFieldItem& rField, USHORT nPara, +//UNUSED2009-05 USHORT nPos, Color*& rTxtColor, +//UNUSED2009-05 Color*& rFldColor ); +//UNUSED2009-05 +//UNUSED2009-05 BOOL ConvertFields(); +//UNUSED2009-05 }; +//UNUSED2009-05 +//UNUSED2009-05 ScFieldChangerEditEngine::ScFieldChangerEditEngine( SfxItemPool* pEnginePoolP, +//UNUSED2009-05 BOOL bDeleteEnginePoolP ) : +//UNUSED2009-05 ScEditEngineDefaulter( pEnginePoolP, bDeleteEnginePoolP ), +//UNUSED2009-05 aExtFileId( TYPE( SvxExtFileField ) ), +//UNUSED2009-05 nConvPara( 0 ), +//UNUSED2009-05 nConvPos( 0 ), +//UNUSED2009-05 bConvert( FALSE ) +//UNUSED2009-05 { +//UNUSED2009-05 } +//UNUSED2009-05 +//UNUSED2009-05 String ScFieldChangerEditEngine::CalcFieldValue( const SvxFieldItem& rField, +//UNUSED2009-05 USHORT nPara, USHORT nPos, Color*& /* rTxtColor */, Color*& /* rFldColor */ ) +//UNUSED2009-05 { +//UNUSED2009-05 const SvxFieldData* pFieldData = rField.GetField(); +//UNUSED2009-05 if ( pFieldData && pFieldData->Type() == aExtFileId ) +//UNUSED2009-05 { +//UNUSED2009-05 bConvert = TRUE; +//UNUSED2009-05 nConvPara = nPara; +//UNUSED2009-05 nConvPos = nPos; +//UNUSED2009-05 } +//UNUSED2009-05 return EMPTY_STRING; +//UNUSED2009-05 } +//UNUSED2009-05 +//UNUSED2009-05 BOOL ScFieldChangerEditEngine::ConvertFields() +//UNUSED2009-05 { +//UNUSED2009-05 BOOL bConverted = FALSE; +//UNUSED2009-05 do +//UNUSED2009-05 { +//UNUSED2009-05 bConvert = FALSE; +//UNUSED2009-05 UpdateFields(); +//UNUSED2009-05 if ( bConvert ) +//UNUSED2009-05 { +//UNUSED2009-05 ESelection aSel( nConvPara, nConvPos, nConvPara, nConvPos+1 ); +//UNUSED2009-05 QuickInsertField( SvxFieldItem( SvxFileField(), EE_FEATURE_FIELD), aSel ); +//UNUSED2009-05 bConverted = TRUE; +//UNUSED2009-05 } +//UNUSED2009-05 } while ( bConvert ); +//UNUSED2009-05 return bConverted; +//UNUSED2009-05 } + +void ScPageHFItem::SetLeftArea( const EditTextObject& rNew ) +{ + delete pLeftArea; + pLeftArea = rNew.Clone(); +} + +//------------------------------------------------------------------------ + +void ScPageHFItem::SetCenterArea( const EditTextObject& rNew ) +{ + delete pCenterArea; + pCenterArea = rNew.Clone(); +} + +//------------------------------------------------------------------------ + +void ScPageHFItem::SetRightArea( const EditTextObject& rNew ) +{ + delete pRightArea; + pRightArea = rNew.Clone(); +} + +void ScPageHFItem::SetArea( EditTextObject *pNew, int nArea ) +{ + switch ( nArea ) + { + case SC_HF_LEFTAREA: delete pLeftArea; pLeftArea = pNew; break; + case SC_HF_CENTERAREA: delete pCenterArea; pCenterArea = pNew; break; + case SC_HF_RIGHTAREA: delete pRightArea; pRightArea = pNew; break; + default: + DBG_ERROR( "New Area?" ); + } +} + +//----------------------------------------------------------------------- +// ScViewObjectModeItem - Darstellungsmodus von ViewObjekten +//----------------------------------------------------------------------- + +ScViewObjectModeItem::ScViewObjectModeItem( USHORT nWhichP ) + : SfxEnumItem( nWhichP, VOBJ_MODE_SHOW ) +{ +} + +//------------------------------------------------------------------------ + +ScViewObjectModeItem::ScViewObjectModeItem( USHORT nWhichP, ScVObjMode eMode ) + : SfxEnumItem( nWhichP, sal::static_int_cast<USHORT>(eMode) ) +{ +} + +//------------------------------------------------------------------------ + +ScViewObjectModeItem::~ScViewObjectModeItem() +{ +} + +//------------------------------------------------------------------------ + +SfxItemPresentation ScViewObjectModeItem::GetPresentation +( + SfxItemPresentation ePres, + SfxMapUnit /* eCoreUnit */, + SfxMapUnit /* ePresUnit */, + String& rText, + const IntlWrapper* /* pIntl */ +) const +{ + String aDel = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(": ")); + rText.Erase(); + + switch ( ePres ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + switch( Which() ) + { + case SID_SCATTR_PAGE_CHARTS: + rText = ScGlobal::GetRscString(STR_VOBJ_CHART); + rText += aDel; + break; + + case SID_SCATTR_PAGE_OBJECTS: + rText = ScGlobal::GetRscString(STR_VOBJ_OBJECT); + rText += aDel; + break; + + case SID_SCATTR_PAGE_DRAWINGS: + rText = ScGlobal::GetRscString(STR_VOBJ_DRAWINGS); + rText += aDel; + break; + + default: + ePres = SFX_ITEM_PRESENTATION_NAMELESS;//das geht immer! + break; + } +// break; // DURCHFALLEN!!! + + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ScGlobal::GetRscString(STR_VOBJ_MODE_SHOW+GetValue()); + break; + + default: + { + // added to avoid warnings + } + } + + return ePres; +} + +//------------------------------------------------------------------------ + +String ScViewObjectModeItem::GetValueText( USHORT nVal ) const +{ + DBG_ASSERT( nVal <= VOBJ_MODE_HIDE, "enum overflow!" ); + + return ScGlobal::GetRscString( STR_VOBJ_MODE_SHOW + (nVal % 2)); +} + +//------------------------------------------------------------------------ + +USHORT ScViewObjectModeItem::GetValueCount() const +{ + return 2; +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScViewObjectModeItem::Clone( SfxItemPool* ) const +{ + return new ScViewObjectModeItem( *this ); +} + +//------------------------------------------------------------------------ + +USHORT ScViewObjectModeItem::GetVersion( USHORT /* nFileVersion */ ) const +{ + return 1; +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScViewObjectModeItem::Create( + SvStream& rStream, + USHORT nVersion ) const +{ + if ( nVersion == 0 ) + { + // alte Version mit AllEnumItem -> mit Mode "Show" erzeugen + return new ScViewObjectModeItem( Which() ); + } + else + { + USHORT nVal; + rStream >> nVal; + + //#i80528# adapt to new range eventually + if((USHORT)VOBJ_MODE_HIDE < nVal) nVal = (USHORT)VOBJ_MODE_SHOW; + + return new ScViewObjectModeItem( Which(), (ScVObjMode)nVal); + } +} + +// ----------------------------------------------------------------------- +// double +// ----------------------------------------------------------------------- + +ScDoubleItem::ScDoubleItem( USHORT nWhichP, double nVal ) + : SfxPoolItem ( nWhichP ), + nValue ( nVal ) +{ +} + +//------------------------------------------------------------------------ + +ScDoubleItem::ScDoubleItem( const ScDoubleItem& rItem ) + : SfxPoolItem ( rItem ) +{ + nValue = rItem.nValue; +} + +//------------------------------------------------------------------------ + +String ScDoubleItem::GetValueText() const +{ + return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("ScDoubleItem")); +} + +//------------------------------------------------------------------------ + +int ScDoubleItem::operator==( const SfxPoolItem& rItem ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rItem ), "unequal Which or Type" ); + const ScDoubleItem& _rItem = (const ScDoubleItem&)rItem; + return int(nValue == _rItem.nValue); + //int(nValue == ((const ScDoubleItem&)rItem).nValue); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScDoubleItem::Clone( SfxItemPool* ) const +{ + return new ScDoubleItem( *this ); +} + +//------------------------------------------------------------------------ + +SfxPoolItem* ScDoubleItem::Create( SvStream& rStream, USHORT /* nVer */ ) const +{ + double nTmp=0; + rStream >> nTmp; + + ScDoubleItem* pItem = new ScDoubleItem( Which(), nTmp ); + + return pItem; +} + +//------------------------------------------------------------------------ + +ScDoubleItem::~ScDoubleItem() +{ +} + + +// ============================================================================ + +ScPageScaleToItem::ScPageScaleToItem() : + SfxPoolItem( ATTR_PAGE_SCALETO ), + mnWidth( 0 ), + mnHeight( 0 ) +{ +} + +ScPageScaleToItem::ScPageScaleToItem( sal_uInt16 nWidth, sal_uInt16 nHeight ) : + SfxPoolItem( ATTR_PAGE_SCALETO ), + mnWidth( nWidth ), + mnHeight( nHeight ) +{ +} + +ScPageScaleToItem::~ScPageScaleToItem() +{ +} + +ScPageScaleToItem* ScPageScaleToItem::Clone( SfxItemPool* ) const +{ + return new ScPageScaleToItem( *this ); +} + +int ScPageScaleToItem::operator==( const SfxPoolItem& rCmp ) const +{ + DBG_ASSERT( SfxPoolItem::operator==( rCmp ), "ScPageScaleToItem::operator== - unequal wid or type" ); + const ScPageScaleToItem& rPageCmp = static_cast< const ScPageScaleToItem& >( rCmp ); + return ((mnWidth == rPageCmp.mnWidth) && (mnHeight == rPageCmp.mnHeight)) ? 1 : 0; +} + +namespace { +void lclAppendScalePageCount( String& rText, sal_uInt16 nPages ) +{ + rText.AppendAscii( ": " ); + if( nPages ) + { + String aPages( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALE_PAGES ) ); + aPages.SearchAndReplaceAscii( "%1", String::CreateFromInt32( nPages ) ); + rText.Append( aPages ); + } + else + rText.Append( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALE_AUTO ) ); +} +} // namespace + +SfxItemPresentation ScPageScaleToItem::GetPresentation( + SfxItemPresentation ePres, SfxMapUnit, SfxMapUnit, XubString& rText, const IntlWrapper* ) const +{ + rText.Erase(); + if( !IsValid() || (ePres == SFX_ITEM_PRESENTATION_NONE) ) + return SFX_ITEM_PRESENTATION_NONE; + + String aName( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALETO ) ); + String aValue( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALE_WIDTH ) ); + lclAppendScalePageCount( aValue, mnWidth ); + aValue.AppendAscii( ", " ).Append( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALE_HEIGHT ) ); + lclAppendScalePageCount( aValue, mnHeight ); + + switch( ePres ) + { + case SFX_ITEM_PRESENTATION_NONE: + break; + + case SFX_ITEM_PRESENTATION_NAMEONLY: + rText = aName; + break; + + case SFX_ITEM_PRESENTATION_NAMELESS: + rText = aValue; + break; + + case SFX_ITEM_PRESENTATION_COMPLETE: + rText.Assign( aName ).AppendAscii( " (" ).Append( aValue ).Append( ')' ); + break; + + default: + DBG_ERRORFILE( "ScPageScaleToItem::GetPresentation - unknown presentation mode" ); + ePres = SFX_ITEM_PRESENTATION_NONE; + } + return ePres; +} + +BOOL ScPageScaleToItem::QueryValue( uno::Any& rAny, BYTE nMemberId ) const +{ + BOOL bRet = TRUE; + switch( nMemberId ) + { + case SC_MID_PAGE_SCALETO_WIDTH: rAny <<= mnWidth; break; + case SC_MID_PAGE_SCALETO_HEIGHT: rAny <<= mnHeight; break; + default: + DBG_ERRORFILE( "ScPageScaleToItem::QueryValue - unknown member ID" ); + bRet = FALSE; + } + return bRet; +} + +BOOL ScPageScaleToItem::PutValue( const uno::Any& rAny, BYTE nMemberId ) +{ + BOOL bRet = FALSE; + switch( nMemberId ) + { + case SC_MID_PAGE_SCALETO_WIDTH: bRet = rAny >>= mnWidth; break; + case SC_MID_PAGE_SCALETO_HEIGHT: bRet = rAny >>= mnHeight; break; + default: + DBG_ERRORFILE( "ScPageScaleToItem::PutValue - unknown member ID" ); + } + return bRet; +} + +// ============================================================================ + + diff --git a/sc/source/core/data/autonamecache.cxx b/sc/source/core/data/autonamecache.cxx new file mode 100644 index 000000000000..2daa10fa29d4 --- /dev/null +++ b/sc/source/core/data/autonamecache.cxx @@ -0,0 +1,111 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: autonamecache.cxx,v $ + * $Revision: 1.4.128.1 $ + * + * 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 <unotools/transliterationwrapper.hxx> + +#include "autonamecache.hxx" +#include "dociter.hxx" +#include "cell.hxx" + +// ----------------------------------------------------------------------- + +ScAutoNameCache::ScAutoNameCache( ScDocument* pD ) : + pDoc( pD ), + nCurrentTab( 0 ) // doesn't matter - aNames is empty +{ +} + +ScAutoNameCache::~ScAutoNameCache() +{ +} + +const ScAutoNameAddresses& ScAutoNameCache::GetNameOccurences( const String& rName, SCTAB nTab ) +{ + if ( nTab != nCurrentTab ) + { + // the lists are valid only for one sheet, so they are cleared when another sheet is used + aNames.clear(); + nCurrentTab = nTab; + } + + ScAutoNameHashMap::const_iterator aFound = aNames.find( rName ); + if ( aFound != aNames.end() ) + return aFound->second; // already initialized + + ScAutoNameAddresses& rAddresses = aNames[rName]; + + ScCellIterator aIter( pDoc, ScRange( 0, 0, nCurrentTab, MAXCOL, MAXROW, nCurrentTab ) ); + for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() ) + { + // don't check code length here, always use the stored result + // (AutoCalc is disabled during CompileXML) + + if ( pCell->HasStringData() ) + { + String aStr; + CellType eType = pCell->GetCellType(); + switch ( eType ) + { + case CELLTYPE_STRING: + ((ScStringCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_FORMULA: + ((ScFormulaCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_EDIT: + ((ScEditCell*)pCell)->GetString( aStr ); + break; + case CELLTYPE_NONE: + case CELLTYPE_VALUE: + case CELLTYPE_NOTE: + case CELLTYPE_SYMBOLS: +#ifdef DBG_UTIL + case CELLTYPE_DESTROYED: +#endif + ; // nothing, prevent compiler warning + break; + } + if ( ScGlobal::GetpTransliteration()->isEqual( aStr, rName ) ) + { + rAddresses.push_back( ScAddress( aIter.GetCol(), aIter.GetRow(), aIter.GetTab() ) ); + } + } + } + + return rAddresses; +} + diff --git a/sc/source/core/data/bcaslot.cxx b/sc/source/core/data/bcaslot.cxx new file mode 100644 index 000000000000..d3d86be58440 --- /dev/null +++ b/sc/source/core/data/bcaslot.cxx @@ -0,0 +1,935 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: bcaslot.cxx,v $ + * $Revision: 1.11 $ + * + * 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 <sfx2/objsh.hxx> +#include <svtools/listener.hxx> +#include <svtools/listeneriter.hxx> + +#include "document.hxx" +#include "brdcst.hxx" +#include "bcaslot.hxx" +#include "scerrors.hxx" +#include "docoptio.hxx" +#include "refupdat.hxx" +#include "table.hxx" + +// Number of slots per dimension +// must be integer divisors of MAXCOLCOUNT respectively MAXROWCOUNT +#define BCA_SLOTS_COL ((MAXCOLCOUNT_DEFINE) / 16) +#if MAXROWCOUNT_DEFINE == 32000 +#define BCA_SLOTS_ROW 256 +#else +#define BCA_SLOTS_ROW ((MAXROWCOUNT_DEFINE) / 128) +#endif +#define BCA_SLOT_COLS ((MAXCOLCOUNT_DEFINE) / BCA_SLOTS_COL) +#define BCA_SLOT_ROWS ((MAXROWCOUNT_DEFINE) / BCA_SLOTS_ROW) +// multiple? +#if (BCA_SLOT_COLS * BCA_SLOTS_COL) != (MAXCOLCOUNT_DEFINE) +#error bad BCA_SLOTS_COL value! +#endif +#if (BCA_SLOT_ROWS * BCA_SLOTS_ROW) != (MAXROWCOUNT_DEFINE) +#error bad BCA_SLOTS_ROW value! +#endif +// size of slot array +#define BCA_SLOTS_DEFINE (BCA_SLOTS_COL * BCA_SLOTS_ROW) +// Arbitrary 2**31/8, assuming size_t can hold at least 2^31 values and +// sizeof_ptr is at most 8 bytes. You'd probably doom your machine's memory +// anyway, once you reached these values.. +#if BCA_SLOTS_DEFINE > 268435456 +#error BCA_SLOTS_DEFINE DOOMed! +#endif +// type safe constant +const SCSIZE BCA_SLOTS = BCA_SLOTS_DEFINE; + +// STATIC DATA ----------------------------------------------------------- + +TYPEINIT1( ScHint, SfxSimpleHint ); +TYPEINIT1( ScAreaChangedHint, SfxHint ); + + +ScBroadcastAreaSlot::ScBroadcastAreaSlot( ScDocument* pDocument, + ScBroadcastAreaSlotMachine* pBASMa ) : + aTmpSeekBroadcastArea( ScRange()), + pDoc( pDocument ), + pBASM( pBASMa ) +{ +} + + +ScBroadcastAreaSlot::~ScBroadcastAreaSlot() +{ + for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); + aIter != aBroadcastAreaTbl.end(); ++aIter) + { + if (!(*aIter)->DecRef()) + delete *aIter; + } +} + + +bool ScBroadcastAreaSlot::CheckHardRecalcStateCondition() const +{ + if ( pDoc->GetHardRecalcState() ) + return true; + if (aBroadcastAreaTbl.size() >= aBroadcastAreaTbl.max_size()) + { // this is more hypothetical now, check existed for old SV_PTRARR_SORT + if ( !pDoc->GetHardRecalcState() ) + { + pDoc->SetHardRecalcState( 1 ); + + SfxObjectShell* pShell = pDoc->GetDocumentShell(); + DBG_ASSERT( pShell, "Missing DocShell :-/" ); + + if ( pShell ) + pShell->SetError( SCWARN_CORE_HARD_RECALC, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( OSL_LOG_PREFIX ) ) ); + + pDoc->SetAutoCalc( FALSE ); + pDoc->SetHardRecalcState( 2 ); + } + return true; + } + return false; +} + + +bool ScBroadcastAreaSlot::StartListeningArea( const ScRange& rRange, + SvtListener* pListener, ScBroadcastArea*& rpArea ) +{ + bool bNewArea = false; + DBG_ASSERT(pListener, "StartListeningArea: pListener Null"); + if (CheckHardRecalcStateCondition()) + return false; + if ( !rpArea ) + { + // Even if most times the area doesn't exist yet and immediately trying + // to new and insert it would save an attempt to find it, on mass + // operations like identical large [HV]LOOKUP() areas the new/delete + // would add quite some penalty for all but the first formula cell. + ScBroadcastAreas::const_iterator aIter( FindBroadcastArea( rRange)); + if (aIter != aBroadcastAreaTbl.end()) + rpArea = *aIter; + else + { + rpArea = new ScBroadcastArea( rRange); + if (aBroadcastAreaTbl.insert( rpArea).second) + { + rpArea->IncRef(); + bNewArea = true; + } + else + { + DBG_ERRORFILE("StartListeningArea: area not found and not inserted in slot?!?"); + delete rpArea; + rpArea = 0; + } + } + if (rpArea) + pListener->StartListening( rpArea->GetBroadcaster()); + } + else + { + if (aBroadcastAreaTbl.insert( rpArea).second) + rpArea->IncRef(); + } + return bNewArea; +} + + +void ScBroadcastAreaSlot::InsertListeningArea( ScBroadcastArea* pArea ) +{ + DBG_ASSERT( pArea, "InsertListeningArea: pArea NULL"); + if (CheckHardRecalcStateCondition()) + return; + if (aBroadcastAreaTbl.insert( pArea).second) + pArea->IncRef(); +} + + +// If rpArea != NULL then no listeners are stopped, only the area is removed +// and the reference count decremented. +void ScBroadcastAreaSlot::EndListeningArea( const ScRange& rRange, + SvtListener* pListener, ScBroadcastArea*& rpArea ) +{ + DBG_ASSERT(pListener, "EndListeningArea: pListener Null"); + if ( !rpArea ) + { + ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange)); + if (aIter == aBroadcastAreaTbl.end()) + return; + rpArea = *aIter; + pListener->EndListening( rpArea->GetBroadcaster() ); + if ( !rpArea->GetBroadcaster().HasListeners() ) + { // if nobody is listening we can dispose it + aBroadcastAreaTbl.erase( aIter); + if ( !rpArea->DecRef() ) + { + delete rpArea; + rpArea = NULL; + } + } + } + else + { + if ( !rpArea->GetBroadcaster().HasListeners() ) + { + ScBroadcastAreas::iterator aIter( FindBroadcastArea( rRange)); + if (aIter == aBroadcastAreaTbl.end()) + return; + DBG_ASSERT( *aIter == rpArea, "EndListeningArea: area pointer mismatch"); + aBroadcastAreaTbl.erase( aIter); + if ( !rpArea->DecRef() ) + { + delete rpArea; + rpArea = NULL; + } + } + } +} + + +ScBroadcastAreas::iterator ScBroadcastAreaSlot::FindBroadcastArea( + const ScRange& rRange ) const +{ + aTmpSeekBroadcastArea.UpdateRange( rRange); + return aBroadcastAreaTbl.find( &aTmpSeekBroadcastArea); +} + + +BOOL ScBroadcastAreaSlot::AreaBroadcast( const ScHint& rHint) const +{ + if (aBroadcastAreaTbl.empty()) + return FALSE; + BOOL bIsBroadcasted = FALSE; + const ScAddress& rAddress = rHint.GetAddress(); + for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()); + aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) + { + ScBroadcastArea* pArea = *aIter; + // A Notify() during broadcast may call EndListeningArea() and thus + // dispose this area if it was the last listener, which would + // invalidate the iterator, hence increment before call. + ++aIter; + const ScRange& rAreaRange = pArea->GetRange(); + if (rAreaRange.In( rAddress)) + { + if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) + { + pArea->GetBroadcaster().Broadcast( rHint); + bIsBroadcasted = TRUE; + } + } + } + return bIsBroadcasted; +} + + +BOOL ScBroadcastAreaSlot::AreaBroadcastInRange( const ScRange& rRange, + const ScHint& rHint) const +{ + if (aBroadcastAreaTbl.empty()) + return FALSE; + BOOL bIsBroadcasted = FALSE; + for (ScBroadcastAreas::const_iterator aIter( aBroadcastAreaTbl.begin()); + aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) + { + ScBroadcastArea* pArea = *aIter; + // A Notify() during broadcast may call EndListeningArea() and thus + // dispose this area if it was the last listener, which would + // invalidate the iterator, hence increment before call. + ++aIter; + const ScRange& rAreaRange = pArea->GetRange(); + if (rAreaRange.Intersects( rRange )) + { + if (!pBASM->IsInBulkBroadcast() || pBASM->InsertBulkArea( pArea)) + { + pArea->GetBroadcaster().Broadcast( rHint); + bIsBroadcasted = TRUE; + } + } + } + return bIsBroadcasted; +} + + +void ScBroadcastAreaSlot::DelBroadcastAreasInRange( const ScRange& rRange ) +{ + if (aBroadcastAreaTbl.empty()) + return; + for (ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); + aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) + { + const ScRange& rAreaRange = (*aIter)->GetRange(); + if (rRange.In( rAreaRange)) + { + ScBroadcastArea* pArea = *aIter; + aBroadcastAreaTbl.erase( aIter++); // erase before modifying + if (!pArea->DecRef()) + { + if (pBASM->IsInBulkBroadcast()) + pBASM->RemoveBulkArea( pArea); + delete pArea; + } + } + else + ++aIter; + } +} + + +void ScBroadcastAreaSlot::UpdateRemove( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if (aBroadcastAreaTbl.empty()) + return; + + SCCOL nCol1, nCol2, theCol1, theCol2; + SCROW nRow1, nRow2, theRow1, theRow2; + SCTAB nTab1, nTab2, theTab1, theTab2; + rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + for ( ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.begin()); + aIter != aBroadcastAreaTbl.end(); /* increment in body */ ) + { + ScBroadcastArea* pArea = *aIter; + if ( pArea->IsInUpdateChain() ) + { + aBroadcastAreaTbl.erase( aIter++); + pArea->DecRef(); + } + else + { + pArea->GetRange().GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, + theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) + { + aBroadcastAreaTbl.erase( aIter++); + pArea->DecRef(); + if (pBASM->IsInBulkBroadcast()) + pBASM->RemoveBulkArea( pArea); + pArea->SetInUpdateChain( TRUE ); + ScBroadcastArea* pUC = pBASM->GetEOUpdateChain(); + if ( pUC ) + pUC->SetUpdateChainNext( pArea ); + else // no tail => no head + pBASM->SetUpdateChain( pArea ); + pBASM->SetEOUpdateChain( pArea ); + } + else + ++aIter; + } + } +} + + +void ScBroadcastAreaSlot::UpdateRemoveArea( ScBroadcastArea* pArea ) +{ + ScBroadcastAreas::iterator aIter( aBroadcastAreaTbl.find( pArea)); + if (aIter == aBroadcastAreaTbl.end()) + return; + if (*aIter != pArea) + DBG_ERRORFILE( "UpdateRemoveArea: area pointer mismatch"); + else + { + aBroadcastAreaTbl.erase( aIter); + pArea->DecRef(); + } +} + + +void ScBroadcastAreaSlot::UpdateInsert( ScBroadcastArea* pArea ) +{ + ::std::pair< ScBroadcastAreas::iterator, bool > aPair = + aBroadcastAreaTbl.insert( pArea ); + if (aPair.second) + pArea->IncRef(); + else + { + // Identical area already exists, add listeners. + ScBroadcastArea* pTarget = *(aPair.first); + if (pArea != pTarget) + { + SvtBroadcaster& rTarget = pTarget->GetBroadcaster(); + SvtListenerIter it( pArea->GetBroadcaster()); + for (SvtListener* pListener = it.GetCurr(); pListener; + pListener = it.GoNext()) + { + pListener->StartListening( rTarget); + } + } + } +} + + +// --- ScBroadcastAreaSlotMachine ------------------------------------- + +ScBroadcastAreaSlotMachine::TableSlots::TableSlots() +{ + ppSlots = new ScBroadcastAreaSlot* [ BCA_SLOTS ]; + memset( ppSlots, 0 , sizeof( ScBroadcastAreaSlot* ) * BCA_SLOTS ); +} + + +ScBroadcastAreaSlotMachine::TableSlots::~TableSlots() +{ + for ( ScBroadcastAreaSlot** pp = ppSlots + BCA_SLOTS; --pp >= ppSlots; /* nothing */ ) + { + if (*pp) + delete *pp; + } + delete [] ppSlots; +} + + +ScBroadcastAreaSlotMachine::ScBroadcastAreaSlotMachine( + ScDocument* pDocument ) : + pBCAlways( NULL ), + pDoc( pDocument ), + pUpdateChain( NULL ), + pEOUpdateChain( NULL ), + nInBulkBroadcast( 0 ) +{ + for (TableSlotsMap::iterator iTab( aTableSlotsMap.begin()); + iTab != aTableSlotsMap.end(); ++iTab) + { + delete (*iTab).second; + } +} + + +ScBroadcastAreaSlotMachine::~ScBroadcastAreaSlotMachine() +{ + delete pBCAlways; +} + + +inline SCSIZE ScBroadcastAreaSlotMachine::ComputeSlotOffset( + const ScAddress& rAddress ) const +{ + SCROW nRow = rAddress.Row(); + SCCOL nCol = rAddress.Col(); + if ( !ValidRow(nRow) || !ValidCol(nCol) ) + { + DBG_ASSERT( FALSE, "Row/Col ungueltig!" ); + return 0; + } + else + return + static_cast<SCSIZE>(nRow) / BCA_SLOT_ROWS + + static_cast<SCSIZE>(nCol) / BCA_SLOT_COLS * BCA_SLOTS_ROW; +} + + +void ScBroadcastAreaSlotMachine::ComputeAreaPoints( const ScRange& rRange, + SCSIZE& rStart, SCSIZE& rEnd, SCSIZE& rRowBreak ) const +{ + rStart = ComputeSlotOffset( rRange.aStart ); + rEnd = ComputeSlotOffset( rRange.aEnd ); + // count of row slots per column minus one + rRowBreak = ComputeSlotOffset( + ScAddress( rRange.aStart.Col(), rRange.aEnd.Row(), 0 ) ) - rStart; +} + + +void ScBroadcastAreaSlotMachine::StartListeningArea( const ScRange& rRange, + SvtListener* pListener ) +{ + if ( rRange == BCA_LISTEN_ALWAYS ) + { + if ( !pBCAlways ) + pBCAlways = new SvtBroadcaster; + pListener->StartListening( *pBCAlways ); + } + else + { + bool bDone = false; + for (SCTAB nTab = rRange.aStart.Tab(); + !bDone && nTab <= rRange.aEnd.Tab(); ++nTab) + { + TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); + if (iTab == aTableSlotsMap.end()) + iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( + nTab, new TableSlots)).first; + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + ScBroadcastArea* pArea = NULL; + while ( !bDone && nOff <= nEnd ) + { + if ( !*pp ) + *pp = new ScBroadcastAreaSlot( pDoc, this ); + if (!pArea) + { + // If the call to StartListeningArea didn't create the + // ScBroadcastArea, listeners were added to an already + // existing identical area that doesn't need to be inserted + // to slots again. + if (!(*pp)->StartListeningArea( rRange, pListener, pArea)) + bDone = true; + } + else + (*pp)->InsertListeningArea( pArea); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + } +} + + +void ScBroadcastAreaSlotMachine::EndListeningArea( const ScRange& rRange, + SvtListener* pListener ) +{ + if ( rRange == BCA_LISTEN_ALWAYS ) + { + DBG_ASSERT( pBCAlways, "ScBroadcastAreaSlotMachine::EndListeningArea: BCA_LISTEN_ALWAYS but none established"); + if ( pBCAlways ) + { + pListener->EndListening( *pBCAlways); + if (!pBCAlways->HasListeners()) + { + delete pBCAlways; + pBCAlways = NULL; + } + } + } + else + { + SCTAB nEndTab = rRange.aEnd.Tab(); + for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) + { + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + ScBroadcastArea* pArea = NULL; + if (nOff == 0 && nEnd == BCA_SLOTS-1) + { + // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they + // happen for insertion and deletion of sheets. + ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; + do + { + if ( *pp ) + (*pp)->EndListeningArea( rRange, pListener, pArea ); + } while (++pp < pStop); + } + else + { + while ( nOff <= nEnd ) + { + if ( *pp ) + (*pp)->EndListeningArea( rRange, pListener, pArea ); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + } + } +} + + +BOOL ScBroadcastAreaSlotMachine::AreaBroadcast( const ScHint& rHint ) const +{ + const ScAddress& rAddress = rHint.GetAddress(); + if ( rAddress == BCA_BRDCST_ALWAYS ) + { + if ( pBCAlways ) + { + pBCAlways->Broadcast( rHint ); + return TRUE; + } + else + return FALSE; + } + else + { + TableSlotsMap::const_iterator iTab( aTableSlotsMap.find( rAddress.Tab())); + if (iTab == aTableSlotsMap.end()) + return FALSE; + ScBroadcastAreaSlot* pSlot = (*iTab).second->getAreaSlot( + ComputeSlotOffset( rAddress)); + if ( pSlot ) + return pSlot->AreaBroadcast( rHint ); + else + return FALSE; + } +} + + +BOOL ScBroadcastAreaSlotMachine::AreaBroadcastInRange( const ScRange& rRange, + const ScHint& rHint ) const +{ + BOOL bBroadcasted = FALSE; + SCTAB nEndTab = rRange.aEnd.Tab(); + for (TableSlotsMap::const_iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) + { + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + while ( nOff <= nEnd ) + { + if ( *pp ) + bBroadcasted |= (*pp)->AreaBroadcastInRange( rRange, rHint ); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + return bBroadcasted; +} + + +void ScBroadcastAreaSlotMachine::DelBroadcastAreasInRange( + const ScRange& rRange ) +{ + SCTAB nEndTab = rRange.aEnd.Tab(); + for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) + { + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + if (nOff == 0 && nEnd == BCA_SLOTS-1) + { + // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they + // happen for insertion and deletion of sheets. + ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; + do + { + if ( *pp ) + (*pp)->DelBroadcastAreasInRange( rRange ); + } while (++pp < pStop); + } + else + { + while ( nOff <= nEnd ) + { + if ( *pp ) + (*pp)->DelBroadcastAreasInRange( rRange ); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + } +} + + +// for all affected: remove, chain, update range, insert, and maybe delete +void ScBroadcastAreaSlotMachine::UpdateBroadcastAreas( + UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + // remove affected and put in chain + SCTAB nEndTab = rRange.aEnd.Tab(); + for (TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + iTab != aTableSlotsMap.end() && (*iTab).first <= nEndTab; ++iTab) + { + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( rRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + if (nOff == 0 && nEnd == BCA_SLOTS-1) + { + // Slightly optimized for 0,0,MAXCOL,MAXROW calls as they + // happen for insertion and deletion of sheets. + ScBroadcastAreaSlot** const pStop = ppSlots + nEnd; + do + { + if ( *pp ) + (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); + } while (++pp < pStop); + } + else + { + while ( nOff <= nEnd ) + { + if ( *pp ) + (*pp)->UpdateRemove( eUpdateRefMode, rRange, nDx, nDy, nDz ); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + } + + // Updating an area's range will modify the hash key, remove areas from all + // affected slots. Will be reinserted later with the updated range. + ScBroadcastArea* pChain = pUpdateChain; + while (pChain) + { + ScBroadcastArea* pArea = pChain; + pChain = pArea->GetUpdateChainNext(); + ScRange aRange( pArea->GetRange()); + // remove from slots + for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab() && pArea->GetRef(); ++nTab) + { + TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); + if (iTab == aTableSlotsMap.end()) + { + DBG_ERRORFILE( "UpdateBroadcastAreas: Where's the TableSlot?!?"); + continue; // for + } + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + while ( nOff <= nEnd && pArea->GetRef() ) + { + if (*pp) + (*pp)->UpdateRemoveArea( pArea); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + + } + + // shift sheets + if (nDz) + { + if (nDz < 0) + { + TableSlotsMap::iterator iDel( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + TableSlotsMap::iterator iTab( aTableSlotsMap.lower_bound( rRange.aStart.Tab() - nDz)); + // Remove sheets, if any, iDel or/and iTab may as well point to end(). + while (iDel != iTab) + { + delete (*iDel).second; + aTableSlotsMap.erase( iDel++); + } + // shift remaining down + while (iTab != aTableSlotsMap.end()) + { + SCTAB nTab = (*iTab).first + nDz; + aTableSlotsMap[nTab] = (*iTab).second; + aTableSlotsMap.erase( iTab++); + } + } + else + { + TableSlotsMap::iterator iStop( aTableSlotsMap.lower_bound( rRange.aStart.Tab())); + if (iStop != aTableSlotsMap.end()) + { + bool bStopIsBegin = (iStop == aTableSlotsMap.begin()); + if (!bStopIsBegin) + --iStop; + TableSlotsMap::iterator iTab( aTableSlotsMap.end()); + --iTab; + while (iTab != iStop) + { + SCTAB nTab = (*iTab).first + nDz; + aTableSlotsMap[nTab] = (*iTab).second; + aTableSlotsMap.erase( iTab--); + } + // Shift the very first, iTab==iStop in this case. + if (bStopIsBegin) + { + SCTAB nTab = (*iTab).first + nDz; + aTableSlotsMap[nTab] = (*iTab).second; + aTableSlotsMap.erase( iStop); + } + } + } + } + + // work off chain + SCCOL nCol1, nCol2, theCol1, theCol2; + SCROW nRow1, nRow2, theRow1, theRow2; + SCTAB nTab1, nTab2, theTab1, theTab2; + rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2); + while ( pUpdateChain ) + { + ScBroadcastArea* pArea = pUpdateChain; + ScRange aRange( pArea->GetRange()); + pUpdateChain = pArea->GetUpdateChainNext(); + + // update range + aRange.GetVars( theCol1, theRow1, theTab1, theCol2, theRow2, theTab2); + if ( ScRefUpdate::Update( pDoc, eUpdateRefMode, + nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, nDx,nDy,nDz, + theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 )) + { + aRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ); + pArea->UpdateRange( aRange ); + pArea->GetBroadcaster().Broadcast( ScAreaChangedHint( aRange ) ); // for DDE + } + + // insert to slots + for (SCTAB nTab = aRange.aStart.Tab(); nTab <= aRange.aEnd.Tab(); ++nTab) + { + TableSlotsMap::iterator iTab( aTableSlotsMap.find( nTab)); + if (iTab == aTableSlotsMap.end()) + iTab = aTableSlotsMap.insert( TableSlotsMap::value_type( + nTab, new TableSlots)).first; + ScBroadcastAreaSlot** ppSlots = (*iTab).second->getSlots(); + SCSIZE nStart, nEnd, nRowBreak; + ComputeAreaPoints( aRange, nStart, nEnd, nRowBreak ); + SCSIZE nOff = nStart; + SCSIZE nBreak = nOff + nRowBreak; + ScBroadcastAreaSlot** pp = ppSlots + nOff; + while ( nOff <= nEnd ) + { + if (!*pp) + *pp = new ScBroadcastAreaSlot( pDoc, this ); + (*pp)->UpdateInsert( pArea ); + if ( nOff < nBreak ) + { + ++nOff; + ++pp; + } + else + { + nStart += BCA_SLOTS_ROW; + nOff = nStart; + pp = ppSlots + nOff; + nBreak = nOff + nRowBreak; + } + } + } + + // unchain + pArea->SetUpdateChainNext( NULL ); + pArea->SetInUpdateChain( FALSE ); + + // Delete if not inserted to any slot. RemoveBulkArea(pArea) was + // already executed in UpdateRemove(). + if (!pArea->GetRef()) + delete pArea; + } + pEOUpdateChain = NULL; +} + + +void ScBroadcastAreaSlotMachine::EnterBulkBroadcast() +{ + ++nInBulkBroadcast; +} + + +void ScBroadcastAreaSlotMachine::LeaveBulkBroadcast() +{ + if (nInBulkBroadcast > 0) + { + if (--nInBulkBroadcast == 0) + ScBroadcastAreasBulk().swap( aBulkBroadcastAreas); + } +} + + +bool ScBroadcastAreaSlotMachine::InsertBulkArea( const ScBroadcastArea* pArea ) +{ + return aBulkBroadcastAreas.insert( pArea ).second; +} + + +size_t ScBroadcastAreaSlotMachine::RemoveBulkArea( const ScBroadcastArea* pArea ) +{ + return aBulkBroadcastAreas.erase( pArea ); +} diff --git a/sc/source/core/data/cell.cxx b/sc/source/core/data/cell.cxx new file mode 100644 index 000000000000..cfd7caafa3f3 --- /dev/null +++ b/sc/source/core/data/cell.cxx @@ -0,0 +1,2036 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: cell.cxx,v $ + * $Revision: 1.44.38.6 $ + * + * 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 <svtools/zforlist.hxx> + +#include "scitems.hxx" +#include "attrib.hxx" +#include "cell.hxx" +#include "compiler.hxx" +#include "interpre.hxx" +#include "document.hxx" +#include "scmatrix.hxx" +#include "dociter.hxx" +#include "docoptio.hxx" +#include "rechead.hxx" +#include "rangenam.hxx" +#include "brdcst.hxx" +#include "ddelink.hxx" +#include "validat.hxx" +#include "progress.hxx" +#include "editutil.hxx" +#include "recursionhelper.hxx" +#include "postit.hxx" +#include "externalrefmgr.hxx" +#include <svx/editobj.hxx> +#include <svtools/intitem.hxx> +#include <svx/flditem.hxx> +#include <svtools/broadcast.hxx> + +using namespace formula; +// More or less arbitrary, of course all recursions must fit into available +// stack space (which is what on all systems we don't know yet?). Choosing a +// lower value may be better than trying a much higher value that also isn't +// sufficient but temporarily leads to high memory consumption. On the other +// hand, if the value fits all recursions, execution is quicker as no resumes +// are necessary. Could be made a configurable option. +// Allow for a year's calendar (366). +const USHORT MAXRECURSION = 400; + +// STATIC DATA ----------------------------------------------------------- + +#ifdef USE_MEMPOOL +// MemPools auf 4k Boundaries - 64 Bytes ausrichten +const USHORT nMemPoolValueCell = (0x8000 - 64) / sizeof(ScValueCell); +const USHORT nMemPoolFormulaCell = (0x8000 - 64) / sizeof(ScFormulaCell); +const USHORT nMemPoolStringCell = (0x4000 - 64) / sizeof(ScStringCell); +const USHORT nMemPoolNoteCell = (0x1000 - 64) / sizeof(ScNoteCell); +IMPL_FIXEDMEMPOOL_NEWDEL( ScValueCell, nMemPoolValueCell, nMemPoolValueCell ) +IMPL_FIXEDMEMPOOL_NEWDEL( ScFormulaCell, nMemPoolFormulaCell, nMemPoolFormulaCell ) +IMPL_FIXEDMEMPOOL_NEWDEL( ScStringCell, nMemPoolStringCell, nMemPoolStringCell ) +IMPL_FIXEDMEMPOOL_NEWDEL( ScNoteCell, nMemPoolNoteCell, nMemPoolNoteCell ) +#endif + +// ============================================================================ + +ScBaseCell::ScBaseCell( CellType eNewType ) : + mpNote( 0 ), + mpBroadcaster( 0 ), + nTextWidth( TEXTWIDTH_DIRTY ), + eCellType( sal::static_int_cast<BYTE>(eNewType) ), + nScriptType( SC_SCRIPTTYPE_UNKNOWN ) +{ +} + +ScBaseCell::ScBaseCell( const ScBaseCell& rCell ) : + mpNote( 0 ), + mpBroadcaster( 0 ), + nTextWidth( rCell.nTextWidth ), + eCellType( rCell.eCellType ), + nScriptType( SC_SCRIPTTYPE_UNKNOWN ) +{ +} + +ScBaseCell::~ScBaseCell() +{ + delete mpNote; + delete mpBroadcaster; + DBG_ASSERT( eCellType == CELLTYPE_DESTROYED, "BaseCell Destructor" ); +} + +namespace { + +ScBaseCell* lclCloneCell( const ScBaseCell& rSrcCell, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) +{ + switch( rSrcCell.GetCellType() ) + { + case CELLTYPE_VALUE: + return new ScValueCell( static_cast< const ScValueCell& >( rSrcCell ) ); + case CELLTYPE_STRING: + return new ScStringCell( static_cast< const ScStringCell& >( rSrcCell ) ); + case CELLTYPE_EDIT: + return new ScEditCell( static_cast< const ScEditCell& >( rSrcCell ), rDestDoc ); + case CELLTYPE_FORMULA: + return new ScFormulaCell( static_cast< const ScFormulaCell& >( rSrcCell ), rDestDoc, rDestPos, nCloneFlags ); + case CELLTYPE_NOTE: + return new ScNoteCell; + default:; + } + DBG_ERROR( "lclCloneCell - unknown cell type" ); + return 0; +} + +} // namespace + +ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, int nCloneFlags ) const +{ + // notes will not be cloned -> cell address only needed for formula cells + ScAddress aDestPos; + if( eCellType == CELLTYPE_FORMULA ) + aDestPos = static_cast< const ScFormulaCell* >( this )->aPos; + return lclCloneCell( *this, rDestDoc, aDestPos, nCloneFlags ); +} + +ScBaseCell* ScBaseCell::CloneWithoutNote( ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const +{ + return lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); +} + +ScBaseCell* ScBaseCell::CloneWithNote( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, int nCloneFlags ) const +{ + ScBaseCell* pNewCell = lclCloneCell( *this, rDestDoc, rDestPos, nCloneFlags ); + if( mpNote ) + { + if( !pNewCell ) + pNewCell = new ScNoteCell; + bool bCloneCaption = (nCloneFlags & SC_CLONECELL_NOCAPTION) == 0; + pNewCell->TakeNote( mpNote->Clone( rOwnPos, rDestDoc, rDestPos, bCloneCaption ) ); + } + return pNewCell; +} + +void ScBaseCell::Delete() +{ + DeleteNote(); + switch (eCellType) + { + case CELLTYPE_VALUE: + delete (ScValueCell*) this; + break; + case CELLTYPE_STRING: + delete (ScStringCell*) this; + break; + case CELLTYPE_EDIT: + delete (ScEditCell*) this; + break; + case CELLTYPE_FORMULA: + delete (ScFormulaCell*) this; + break; + case CELLTYPE_NOTE: + delete (ScNoteCell*) this; + break; + default: + DBG_ERROR("Unbekannter Zellentyp"); + break; + } +} + +bool ScBaseCell::IsBlank( bool bIgnoreNotes ) const +{ + return (eCellType == CELLTYPE_NOTE) && (bIgnoreNotes || !mpNote); +} + +void ScBaseCell::TakeNote( ScPostIt* pNote ) +{ + delete mpNote; + mpNote = pNote; +} + +ScPostIt* ScBaseCell::ReleaseNote() +{ + ScPostIt* pNote = mpNote; + mpNote = 0; + return pNote; +} + +void ScBaseCell::DeleteNote() +{ + DELETEZ( mpNote ); +} + +void ScBaseCell::TakeBroadcaster( SvtBroadcaster* pBroadcaster ) +{ + delete mpBroadcaster; + mpBroadcaster = pBroadcaster; +} + +SvtBroadcaster* ScBaseCell::ReleaseBroadcaster() +{ + SvtBroadcaster* pBroadcaster = mpBroadcaster; + mpBroadcaster = 0; + return pBroadcaster; +} + +void ScBaseCell::DeleteBroadcaster() +{ + DELETEZ( mpBroadcaster ); +} + +ScBaseCell* ScBaseCell::CreateTextCell( const String& rString, ScDocument* pDoc ) +{ + if ( rString.Search('\n') != STRING_NOTFOUND || rString.Search(CHAR_CR) != STRING_NOTFOUND ) + return new ScEditCell( rString, pDoc ); + else + return new ScStringCell( rString ); +} + +void ScBaseCell::StartListeningTo( ScDocument* pDoc ) +{ + if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() + && !pDoc->GetNoListening() + && !((ScFormulaCell*)this)->IsInChangeTrack() + ) + { + pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert... + + ScFormulaCell* pFormCell = (ScFormulaCell*)this; + ScTokenArray* pArr = pFormCell->GetCode(); + if( pArr->IsRecalcModeAlways() ) + pDoc->StartListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); + else + { + pArr->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) + { + StackVar eType = t->GetType(); + ScSingleRefData& rRef1 = t->GetSingleRef(); + ScSingleRefData& rRef2 = (eType == svDoubleRef ? + t->GetDoubleRef().Ref2 : rRef1); + switch( eType ) + { + case svSingleRef: + rRef1.CalcAbsIfRel( pFormCell->aPos ); + if ( rRef1.Valid() ) + { + pDoc->StartListeningCell( + ScAddress( rRef1.nCol, + rRef1.nRow, + rRef1.nTab ), pFormCell ); + } + break; + case svDoubleRef: + t->CalcAbsIfRel( pFormCell->aPos ); + if ( rRef1.Valid() && rRef2.Valid() ) + { + if ( t->GetOpCode() == ocColRowNameAuto ) + { // automagically + if ( rRef1.IsColRel() ) + { // ColName + pDoc->StartListeningArea( ScRange ( + 0, + rRef1.nRow, + rRef1.nTab, + MAXCOL, + rRef2.nRow, + rRef2.nTab ), pFormCell ); + } + else + { // RowName + pDoc->StartListeningArea( ScRange ( + rRef1.nCol, + 0, + rRef1.nTab, + rRef2.nCol, + MAXROW, + rRef2.nTab ), pFormCell ); + } + } + else + { + pDoc->StartListeningArea( ScRange ( + rRef1.nCol, + rRef1.nRow, + rRef1.nTab, + rRef2.nCol, + rRef2.nRow, + rRef2.nTab ), pFormCell ); + } + } + break; + default: + ; // nothing + } + } + } + pFormCell->SetNeedsListening( FALSE); + } +} + +// pArr gesetzt -> Referenzen von anderer Zelle nehmen +// dann muss auch aPos uebergeben werden! + +void ScBaseCell::EndListeningTo( ScDocument* pDoc, ScTokenArray* pArr, + ScAddress aPos ) +{ + if ( eCellType == CELLTYPE_FORMULA && !pDoc->IsClipOrUndo() + && !((ScFormulaCell*)this)->IsInChangeTrack() + ) + { + pDoc->SetDetectiveDirty(TRUE); // es hat sich was geaendert... + + ScFormulaCell* pFormCell = (ScFormulaCell*)this; + if( pFormCell->GetCode()->IsRecalcModeAlways() ) + pDoc->EndListeningArea( BCA_LISTEN_ALWAYS, pFormCell ); + else + { + if (!pArr) + { + pArr = pFormCell->GetCode(); + aPos = pFormCell->aPos; + } + pArr->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pArr->GetNextReferenceRPN()) ) != NULL ) + { + StackVar eType = t->GetType(); + ScSingleRefData& rRef1 = t->GetSingleRef(); + ScSingleRefData& rRef2 = (eType == svDoubleRef ? + t->GetDoubleRef().Ref2 : rRef1); + switch( eType ) + { + case svSingleRef: + rRef1.CalcAbsIfRel( aPos ); + if ( rRef1.Valid() ) + { + pDoc->EndListeningCell( + ScAddress( rRef1.nCol, + rRef1.nRow, + rRef1.nTab ), pFormCell ); + } + break; + case svDoubleRef: + t->CalcAbsIfRel( aPos ); + if ( rRef1.Valid() && rRef2.Valid() ) + { + if ( t->GetOpCode() == ocColRowNameAuto ) + { // automagically + if ( rRef1.IsColRel() ) + { // ColName + pDoc->EndListeningArea( ScRange ( + 0, + rRef1.nRow, + rRef1.nTab, + MAXCOL, + rRef2.nRow, + rRef2.nTab ), pFormCell ); + } + else + { // RowName + pDoc->EndListeningArea( ScRange ( + rRef1.nCol, + 0, + rRef1.nTab, + rRef2.nCol, + MAXROW, + rRef2.nTab ), pFormCell ); + } + } + else + { + pDoc->EndListeningArea( ScRange ( + rRef1.nCol, + rRef1.nRow, + rRef1.nTab, + rRef2.nCol, + rRef2.nRow, + rRef2.nTab ), pFormCell ); + } + } + break; + default: + ; // nothing + } + } + } + } +} + + +USHORT ScBaseCell::GetErrorCode() const +{ + switch ( eCellType ) + { + case CELLTYPE_FORMULA : + return ((ScFormulaCell*)this)->GetErrCode(); + default: + return 0; + } +} + + +BOOL ScBaseCell::HasEmptyData() const +{ + switch ( eCellType ) + { + case CELLTYPE_NOTE : + return TRUE; + case CELLTYPE_FORMULA : + return ((ScFormulaCell*)this)->IsEmpty(); + default: + return FALSE; + } +} + + +BOOL ScBaseCell::HasValueData() const +{ + switch ( eCellType ) + { + case CELLTYPE_VALUE : + return TRUE; + case CELLTYPE_FORMULA : + return ((ScFormulaCell*)this)->IsValue(); + default: + return FALSE; + } +} + + +BOOL ScBaseCell::HasStringData() const +{ + switch ( eCellType ) + { + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + return TRUE; + case CELLTYPE_FORMULA : + return !((ScFormulaCell*)this)->IsValue(); + default: + return FALSE; + } +} + +String ScBaseCell::GetStringData() const +{ + String aStr; + switch ( eCellType ) + { + case CELLTYPE_STRING: + ((const ScStringCell*)this)->GetString( aStr ); + break; + case CELLTYPE_EDIT: + ((const ScEditCell*)this)->GetString( aStr ); + break; + case CELLTYPE_FORMULA: + ((ScFormulaCell*)this)->GetString( aStr ); // an der Formelzelle nicht-const + break; + } + return aStr; +} + +// static +BOOL ScBaseCell::CellEqual( const ScBaseCell* pCell1, const ScBaseCell* pCell2 ) +{ + CellType eType1 = CELLTYPE_NONE; + CellType eType2 = CELLTYPE_NONE; + if ( pCell1 ) + { + eType1 = pCell1->GetCellType(); + if (eType1 == CELLTYPE_EDIT) + eType1 = CELLTYPE_STRING; + else if (eType1 == CELLTYPE_NOTE) + eType1 = CELLTYPE_NONE; + } + if ( pCell2 ) + { + eType2 = pCell2->GetCellType(); + if (eType2 == CELLTYPE_EDIT) + eType2 = CELLTYPE_STRING; + else if (eType2 == CELLTYPE_NOTE) + eType2 = CELLTYPE_NONE; + } + if ( eType1 != eType2 ) + return FALSE; + + switch ( eType1 ) // beide Typen gleich + { + case CELLTYPE_NONE: // beide leer + return TRUE; + case CELLTYPE_VALUE: // wirklich Value-Zellen + return ( ((const ScValueCell*)pCell1)->GetValue() == + ((const ScValueCell*)pCell2)->GetValue() ); + case CELLTYPE_STRING: // String oder Edit + { + String aText1; + if ( pCell1->GetCellType() == CELLTYPE_STRING ) + ((const ScStringCell*)pCell1)->GetString(aText1); + else + ((const ScEditCell*)pCell1)->GetString(aText1); + String aText2; + if ( pCell2->GetCellType() == CELLTYPE_STRING ) + ((const ScStringCell*)pCell2)->GetString(aText2); + else + ((const ScEditCell*)pCell2)->GetString(aText2); + return ( aText1 == aText2 ); + } + case CELLTYPE_FORMULA: + { + //! eingefuegte Zeilen / Spalten beruecksichtigen !!!!! + //! Vergleichsfunktion an der Formelzelle ??? + //! Abfrage mit ScColumn::SwapRow zusammenfassen! + + ScTokenArray* pCode1 = ((ScFormulaCell*)pCell1)->GetCode(); + ScTokenArray* pCode2 = ((ScFormulaCell*)pCell2)->GetCode(); + + if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN + { + BOOL bEqual = TRUE; + USHORT nLen = pCode1->GetLen(); + FormulaToken** ppToken1 = pCode1->GetArray(); + FormulaToken** ppToken2 = pCode2->GetArray(); + for (USHORT i=0; i<nLen; i++) + if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) ) + { + bEqual = FALSE; + break; + } + + if (bEqual) + return TRUE; + } + + return FALSE; // unterschiedlich lang oder unterschiedliche Tokens + } + default: + DBG_ERROR("huch, was fuer Zellen???"); + } + return FALSE; +} + +// ============================================================================ + +ScNoteCell::ScNoteCell( SvtBroadcaster* pBC ) : + ScBaseCell( CELLTYPE_NOTE ) +{ + TakeBroadcaster( pBC ); +} + +ScNoteCell::ScNoteCell( ScPostIt* pNote, SvtBroadcaster* pBC ) : + ScBaseCell( CELLTYPE_NOTE ) +{ + TakeNote( pNote ); + TakeBroadcaster( pBC ); +} + +#ifdef DBG_UTIL +ScNoteCell::~ScNoteCell() +{ + eCellType = CELLTYPE_DESTROYED; +} +#endif + +// ============================================================================ + +ScValueCell::ScValueCell() : + ScBaseCell( CELLTYPE_VALUE ), + mfValue( 0.0 ) +{ +} + +ScValueCell::ScValueCell( double fValue ) : + ScBaseCell( CELLTYPE_VALUE ), + mfValue( fValue ) +{ +} + +#ifdef DBG_UTIL +ScValueCell::~ScValueCell() +{ + eCellType = CELLTYPE_DESTROYED; +} +#endif + +// ============================================================================ + +ScStringCell::ScStringCell() : + ScBaseCell( CELLTYPE_STRING ) +{ +} + +ScStringCell::ScStringCell( const String& rString ) : + ScBaseCell( CELLTYPE_STRING ), + maString( rString.intern() ) +{ +} + +#ifdef DBG_UTIL +ScStringCell::~ScStringCell() +{ + eCellType = CELLTYPE_DESTROYED; +} +#endif + +// ============================================================================ + +// +// ScFormulaCell +// + +ScFormulaCell::ScFormulaCell() : + ScBaseCell( CELLTYPE_FORMULA ), + eTempGrammar( FormulaGrammar::GRAM_DEFAULT), + pCode( NULL ), + pDocument( NULL ), + pPrevious(0), + pNext(0), + pPreviousTrack(0), + pNextTrack(0), + nFormatIndex(0), + nFormatType( NUMBERFORMAT_NUMBER ), + nSeenInIteration(0), + cMatrixFlag ( MM_NONE ), + bDirty( FALSE ), + bChanged( FALSE ), + bRunning( FALSE ), + bCompile( FALSE ), + bSubTotal( FALSE ), + bIsIterCell( FALSE ), + bInChangeTrack( FALSE ), + bTableOpDirty( FALSE ), + bNeedListening( FALSE ), + aPos(0,0,0) +{ +} + +ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, + const String& rFormula, + const FormulaGrammar::Grammar eGrammar, + BYTE cMatInd ) : + ScBaseCell( CELLTYPE_FORMULA ), + eTempGrammar( eGrammar), + pCode( NULL ), + pDocument( pDoc ), + pPrevious(0), + pNext(0), + pPreviousTrack(0), + pNextTrack(0), + nFormatIndex(0), + nFormatType( NUMBERFORMAT_NUMBER ), + nSeenInIteration(0), + cMatrixFlag ( cMatInd ), + bDirty( TRUE ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cMatInd != 0 + bChanged( FALSE ), + bRunning( FALSE ), + bCompile( FALSE ), + bSubTotal( FALSE ), + bIsIterCell( FALSE ), + bInChangeTrack( FALSE ), + bTableOpDirty( FALSE ), + bNeedListening( FALSE ), + aPos( rPos ) +{ + Compile( rFormula, TRUE, eGrammar ); // bNoListening, Insert does that +} + +// Wird von den Importfiltern verwendet + +ScFormulaCell::ScFormulaCell( ScDocument* pDoc, const ScAddress& rPos, + const ScTokenArray* pArr, + const FormulaGrammar::Grammar eGrammar, BYTE cInd ) : + ScBaseCell( CELLTYPE_FORMULA ), + eTempGrammar( eGrammar), + pCode( pArr ? new ScTokenArray( *pArr ) : new ScTokenArray ), + pDocument( pDoc ), + pPrevious(0), + pNext(0), + pPreviousTrack(0), + pNextTrack(0), + nFormatIndex(0), + nFormatType( NUMBERFORMAT_NUMBER ), + nSeenInIteration(0), + cMatrixFlag ( cInd ), + bDirty( NULL != pArr ), // -> wg. Benutzung im Fkt.AutoPiloten, war: cInd != 0 + bChanged( FALSE ), + bRunning( FALSE ), + bCompile( FALSE ), + bSubTotal( FALSE ), + bIsIterCell( FALSE ), + bInChangeTrack( FALSE ), + bTableOpDirty( FALSE ), + bNeedListening( FALSE ), + aPos( rPos ) +{ + // UPN-Array erzeugen + if( pCode->GetLen() && !pCode->GetCodeError() && !pCode->GetCodeLen() ) + { + ScCompiler aComp( pDocument, aPos, *pCode); + aComp.SetGrammar(eTempGrammar); + bSubTotal = aComp.CompileTokenArray(); + nFormatType = aComp.GetNumFormatType(); + } + else + { + pCode->Reset(); + if ( pCode->GetNextOpCodeRPN( ocSubTotal ) ) + bSubTotal = TRUE; + } +} + +ScFormulaCell::ScFormulaCell( const ScFormulaCell& rCell, ScDocument& rDoc, const ScAddress& rPos, int nCloneFlags ) : + ScBaseCell( rCell ), + SvtListener(), + aResult( rCell.aResult ), + eTempGrammar( rCell.eTempGrammar), + pDocument( &rDoc ), + pPrevious(0), + pNext(0), + pPreviousTrack(0), + pNextTrack(0), + nFormatIndex( &rDoc == rCell.pDocument ? rCell.nFormatIndex : 0 ), + nFormatType( rCell.nFormatType ), + nSeenInIteration(0), + cMatrixFlag ( rCell.cMatrixFlag ), + bDirty( rCell.bDirty ), + bChanged( rCell.bChanged ), + bRunning( FALSE ), + bCompile( rCell.bCompile ), + bSubTotal( rCell.bSubTotal ), + bIsIterCell( FALSE ), + bInChangeTrack( FALSE ), + bTableOpDirty( FALSE ), + bNeedListening( FALSE ), + aPos( rPos ) +{ + pCode = rCell.pCode->Clone(); + + if ( nCloneFlags & SC_CLONECELL_ADJUST3DREL ) + pCode->ReadjustRelative3DReferences( rCell.aPos, aPos ); + + // evtl. Fehler zuruecksetzen und neu kompilieren + // nicht im Clipboard - da muss das Fehlerflag erhalten bleiben + // Spezialfall Laenge=0: als Fehlerzelle erzeugt, dann auch Fehler behalten + if ( pCode->GetCodeError() && !pDocument->IsClipboard() && pCode->GetLen() ) + { + pCode->SetCodeError( 0 ); + bCompile = TRUE; + } + //! Compile ColRowNames on URM_MOVE/URM_COPY _after_ UpdateReference + BOOL bCompileLater = FALSE; + BOOL bClipMode = rCell.pDocument->IsClipboard(); + if( !bCompile ) + { // Name references with references and ColRowNames + pCode->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceOrName()) ) != NULL && !bCompile ) + { + if ( t->GetOpCode() == ocExternalRef ) + { + // External name, cell, and area references. + bCompile = true; + } + else if ( t->GetType() == svIndex ) + { + ScRangeData* pRangeData = rDoc.GetRangeName()->FindIndex( t->GetIndex() ); + if( pRangeData ) + { + if( pRangeData->HasReferences() ) + bCompile = TRUE; + } + else + bCompile = TRUE; // invalid reference! + } + else if ( t->GetOpCode() == ocColRowName ) + { + bCompile = TRUE; // new lookup needed + bCompileLater = bClipMode; + } + } + } + if( bCompile ) + { + if ( !bCompileLater && bClipMode ) + { + // Merging ranges needs the actual positions after UpdateReference. + // ColRowNames need new lookup after positions are adjusted. + bCompileLater = pCode->HasOpCode( ocRange) || pCode->HasOpCode( ocColRowName); + } + if ( !bCompileLater ) + { + // bNoListening, not at all if in Clipboard/Undo, + // and not from Clipboard either, instead after Insert(Clone) and UpdateReference. + CompileTokenArray( TRUE ); + } + } + + if( nCloneFlags & SC_CLONECELL_STARTLISTENING ) + StartListeningTo( &rDoc ); +} + +ScFormulaCell::~ScFormulaCell() +{ + pDocument->RemoveFromFormulaTree( this ); + delete pCode; +#ifdef DBG_UTIL + eCellType = CELLTYPE_DESTROYED; +#endif +} + +void ScFormulaCell::GetFormula( rtl::OUStringBuffer& rBuffer, + const FormulaGrammar::Grammar eGrammar ) const +{ + if( pCode->GetCodeError() && !pCode->GetLen() ) + { + rBuffer = rtl::OUStringBuffer( ScGlobal::GetErrorString( pCode->GetCodeError())); + return; + } + else if( cMatrixFlag == MM_REFERENCE ) + { + // Reference to another cell that contains a matrix formula. + pCode->Reset(); + ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + if( p ) + { + /* FIXME: original GetFormula() code obtained + * pCell only if (!this->IsInChangeTrack()), + * GetEnglishFormula() omitted that test. + * Can we live without in all cases? */ + ScBaseCell* pCell; + ScSingleRefData& rRef = p->GetSingleRef(); + rRef.CalcAbsIfRel( aPos ); + if ( rRef.Valid() ) + pCell = pDocument->GetCell( ScAddress( rRef.nCol, + rRef.nRow, rRef.nTab ) ); + else + pCell = NULL; + if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) + { + ((ScFormulaCell*)pCell)->GetFormula( rBuffer, eGrammar); + return; + } + else + { + ScCompiler aComp( pDocument, aPos, *pCode); + aComp.SetGrammar(eGrammar); + aComp.CreateStringFromTokenArray( rBuffer ); + } + } + else + { + DBG_ERROR("ScFormulaCell::GetFormula: not a matrix"); + } + } + else + { + ScCompiler aComp( pDocument, aPos, *pCode); + aComp.SetGrammar(eGrammar); + aComp.CreateStringFromTokenArray( rBuffer ); + } + + sal_Unicode ch('='); + rBuffer.insert( 0, &ch, 1 ); + if( cMatrixFlag ) + { + sal_Unicode ch2('{'); + rBuffer.insert( 0, &ch2, 1); + rBuffer.append( sal_Unicode('}')); + } +} + +void ScFormulaCell::GetFormula( String& rFormula, const FormulaGrammar::Grammar eGrammar ) const +{ + rtl::OUStringBuffer rBuffer( rFormula ); + GetFormula( rBuffer, eGrammar ); + rFormula = rBuffer; +} + +void ScFormulaCell::GetResultDimensions( SCSIZE& rCols, SCSIZE& rRows ) +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + + const ScMatrix* pMat = NULL; + if (!pCode->GetCodeError() && aResult.GetType() == svMatrixCell && + ((pMat = static_cast<const ScToken*>(aResult.GetToken().get())->GetMatrix()) != 0)) + pMat->GetDimensions( rCols, rRows ); + else + { + rCols = 0; + rRows = 0; + } +} + +void ScFormulaCell::Compile( const String& rFormula, BOOL bNoListening, + const FormulaGrammar::Grammar eGrammar ) +{ + if ( pDocument->IsClipOrUndo() ) return; + BOOL bWasInFormulaTree = pDocument->IsInFormulaTree( this ); + if ( bWasInFormulaTree ) + pDocument->RemoveFromFormulaTree( this ); + // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein + if ( pCode ) + pCode->Clear(); + ScTokenArray* pCodeOld = pCode; + ScCompiler aComp( pDocument, aPos); + aComp.SetGrammar(eGrammar); + pCode = aComp.CompileString( rFormula ); + if ( pCodeOld ) + delete pCodeOld; + if( !pCode->GetCodeError() ) + { + if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() && rFormula == aResult.GetHybridFormula() ) + { // #65994# nicht rekursiv CompileTokenArray/Compile/CompileTokenArray + if ( rFormula.GetChar(0) == '=' ) + pCode->AddBad( rFormula.GetBuffer() + 1 ); + else + pCode->AddBad( rFormula.GetBuffer() ); + } + bCompile = TRUE; + CompileTokenArray( bNoListening ); + } + else + { + bChanged = TRUE; + SetTextWidth( TEXTWIDTH_DIRTY ); + SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); + } + if ( bWasInFormulaTree ) + pDocument->PutInFormulaTree( this ); +} + + +void ScFormulaCell::CompileTokenArray( BOOL bNoListening ) +{ + // Not already compiled? + if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) + Compile( aResult.GetHybridFormula(), bNoListening, eTempGrammar); + else if( bCompile && !pDocument->IsClipOrUndo() && !pCode->GetCodeError() ) + { + // RPN length may get changed + BOOL bWasInFormulaTree = pDocument->IsInFormulaTree( this ); + if ( bWasInFormulaTree ) + pDocument->RemoveFromFormulaTree( this ); + + // Loading from within filter? No listening yet! + if( pDocument->IsInsertingFromOtherDoc() ) + bNoListening = TRUE; + + if( !bNoListening && pCode->GetCodeLen() ) + EndListeningTo( pDocument ); + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + bSubTotal = aComp.CompileTokenArray(); + if( !pCode->GetCodeError() ) + { + nFormatType = aComp.GetNumFormatType(); + nFormatIndex = 0; + bChanged = TRUE; + aResult.SetToken( NULL); + bCompile = FALSE; + if ( !bNoListening ) + StartListeningTo( pDocument ); + } + if ( bWasInFormulaTree ) + pDocument->PutInFormulaTree( this ); + } +} + + +void ScFormulaCell::CompileXML( ScProgress& rProgress ) +{ + if ( cMatrixFlag == MM_REFERENCE ) + { // is already token code via ScDocFunc::EnterMatrix, ScDocument::InsertMatrixFormula + // just establish listeners + StartListeningTo( pDocument ); + return ; + } + + ScCompiler aComp( pDocument, aPos, *pCode); + aComp.SetGrammar(eTempGrammar); + String aFormula, aFormulaNmsp; + aComp.CreateStringFromXMLTokenArray( aFormula, aFormulaNmsp ); + pDocument->DecXMLImportedFormulaCount( aFormula.Len() ); + rProgress.SetStateCountDownOnPercent( pDocument->GetXMLImportedFormulaCount() ); + // pCode darf fuer Abfragen noch nicht geloescht, muss aber leer sein + if ( pCode ) + pCode->Clear(); + ScTokenArray* pCodeOld = pCode; + pCode = aComp.CompileString( aFormula, aFormulaNmsp ); + delete pCodeOld; + if( !pCode->GetCodeError() ) + { + if ( !pCode->GetLen() ) + { + if ( aFormula.GetChar(0) == '=' ) + pCode->AddBad( aFormula.GetBuffer() + 1 ); + else + pCode->AddBad( aFormula.GetBuffer() ); + } + bSubTotal = aComp.CompileTokenArray(); + if( !pCode->GetCodeError() ) + { + nFormatType = aComp.GetNumFormatType(); + nFormatIndex = 0; + bChanged = TRUE; + bCompile = FALSE; + StartListeningTo( pDocument ); + } + } + else + { + bChanged = TRUE; + SetTextWidth( TEXTWIDTH_DIRTY ); + SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); + } + + // Same as in Load: after loading, it must be known if ocMacro is in any formula + // (for macro warning, CompileXML is called at the end of loading XML file) + if ( !pDocument->GetHasMacroFunc() && pCode->HasOpCodeRPN( ocMacro ) ) + pDocument->SetHasMacroFunc( TRUE ); +} + + +void ScFormulaCell::CalcAfterLoad() +{ + BOOL bNewCompiled = FALSE; + // Falls ein Calc 1.0-Doc eingelesen wird, haben wir ein Ergebnis, + // aber kein TokenArray + if( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) + { + Compile( aResult.GetHybridFormula(), TRUE, eTempGrammar); + aResult.SetToken( NULL); + bDirty = TRUE; + bNewCompiled = TRUE; + } + // Das UPN-Array wird nicht erzeugt, wenn ein Calc 3.0-Doc eingelesen + // wurde, da die RangeNames erst jetzt existieren. + if( pCode->GetLen() && !pCode->GetCodeLen() && !pCode->GetCodeError() ) + { + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + bSubTotal = aComp.CompileTokenArray(); + nFormatType = aComp.GetNumFormatType(); + nFormatIndex = 0; + bDirty = TRUE; + bCompile = FALSE; + bNewCompiled = TRUE; + } + // irgendwie koennen unter os/2 mit rotter FPU-Exception /0 ohne Err503 + // gespeichert werden, woraufhin spaeter im NumberFormatter die BLC Lib + // bei einem fabs(-NAN) abstuerzt (#32739#) + // hier fuer alle Systeme ausbuegeln, damit da auch Err503 steht + if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) + { + DBG_ERRORFILE("Formelzelle INFINITY !!! Woher kommt das Dokument?"); + aResult.SetResultError( errIllegalFPOperation ); + bDirty = TRUE; + } + // DoubleRefs bei binaeren Operatoren waren vor v5.0 immer Matrix, + // jetzt nur noch wenn in Matrixformel, sonst implizite Schnittmenge + if ( pDocument->GetSrcVersion() < SC_MATRIX_DOUBLEREF && + GetMatrixFlag() == MM_NONE && pCode->HasMatrixDoubleRefOps() ) + { + cMatrixFlag = MM_FORMULA; + SetMatColsRows( 1, 1); + } + // Muss die Zelle berechnet werden? + // Nach Load koennen Zellen einen Fehlercode enthalten, auch dann + // Listener starten und ggbf. neu berechnen wenn nicht RECALCMODE_NORMAL + if( !bNewCompiled || !pCode->GetCodeError() ) + { + StartListeningTo( pDocument ); + if( !pCode->IsRecalcModeNormal() ) + bDirty = TRUE; + } + if ( pCode->IsRecalcModeAlways() ) + { // zufall(), heute(), jetzt() bleiben immer im FormulaTree, damit sie + // auch bei jedem F9 berechnet werden. + bDirty = TRUE; + } + // Noch kein SetDirty weil noch nicht alle Listener bekannt, erst in + // SetDirtyAfterLoad. +} + + +bool ScFormulaCell::MarkUsedExternalReferences() +{ + return pCode && pDocument->MarkUsedExternalReferences( *pCode); +} + + +// FIXME: set to 0 +#define erDEBUGDOT 0 +// If set to 1, write output that's suitable for graphviz tools like dot. +// Only node1 -> node2 entries are written, you'll have to manually surround +// the file content with [strict] digraph name { ... } +// The ``strict'' keyword might be necessary in case of multiple identical +// paths like they occur in iterations, otherwise dot may consume too much +// memory when generating the layout, or you'll get unreadable output. On the +// other hand, information about recurring calculation is lost then. +// Generates output only if variable nDebug is set in debugger, see below. +// FIXME: currently doesn't cope with iterations and recursions. Code fragments +// are a leftover from a previous debug session, meant as a pointer. +#if erDEBUGDOT +#include <cstdio> +using ::std::fopen; +using ::std::fprintf; +#include <vector> +static const char aDebugDotFile[] = "ttt_debug.dot"; +#endif + +void ScFormulaCell::Interpret() +{ + +#if erDEBUGDOT + static int nDebug = 0; + static const int erDEBUGDOTRUN = 3; + static FILE* pDebugFile = 0; + static sal_Int32 nDebugRootCount = 0; + static unsigned int nDebugPathCount = 0; + static ScAddress aDebugLastPos( ScAddress::INITIALIZE_INVALID); + static ScAddress aDebugThisPos( ScAddress::INITIALIZE_INVALID); + typedef ::std::vector< ByteString > DebugVector; + static DebugVector aDebugVec; + class DebugElement + { + public: + static void push( ScFormulaCell* pCell ) + { + aDebugThisPos = pCell->aPos; + if (aDebugVec.empty()) + { + ByteString aR( "root_"); + aR += ByteString::CreateFromInt32( ++nDebugRootCount); + aDebugVec.push_back( aR); + } + String aStr; + pCell->aPos.Format( aStr, SCA_VALID | SCA_TAB_3D, pCell->GetDocument(), + pCell->GetDocument()->GetAddressConvention() ); + ByteString aB( aStr, RTL_TEXTENCODING_UTF8); + aDebugVec.push_back( aB); + } + static void pop() + { + aDebugLastPos = aDebugThisPos; + if (!aDebugVec.empty()) + { + aDebugVec.pop_back(); + if (aDebugVec.size() == 1) + { + aDebugVec.pop_back(); + aDebugLastPos = ScAddress( ScAddress::INITIALIZE_INVALID); + } + } + } + DebugElement( ScFormulaCell* p ) { push(p); } + ~DebugElement() { pop(); } + }; + class DebugDot + { + public: + static void out( const char* pColor ) + { + if (nDebug != erDEBUGDOTRUN) + return; + char pColorString[256]; + sprintf( pColorString, (*pColor ? + ",color=\"%s\",fontcolor=\"%s\"" : "%s%s"), pColor, + pColor); + size_t n = aDebugVec.size(); + fprintf( pDebugFile, + "\"%s\" -> \"%s\" [label=\"%u\"%s]; // v:%d\n", + aDebugVec[n-2].GetBuffer(), aDebugVec[n-1].GetBuffer(), + ++nDebugPathCount, pColorString, n-1); + fflush( pDebugFile); + } + }; + #define erDEBUGDOT_OUT( p ) (DebugDot::out(p)) + #define erDEBUGDOT_ELEMENT_PUSH( p ) (DebugElement::push(p)) + #define erDEBUGDOT_ELEMENT_POP() (DebugElement::pop()) +#else + #define erDEBUGDOT_OUT( p ) + #define erDEBUGDOT_ELEMENT_PUSH( p ) + #define erDEBUGDOT_ELEMENT_POP() +#endif + + if (!IsDirtyOrInTableOpDirty() || pDocument->GetRecursionHelper().IsInReturn()) + return; // no double/triple processing + + //! HACK: + // Wenn der Aufruf aus einem Reschedule im DdeLink-Update kommt, dirty stehenlassen + // Besser: Dde-Link Update ohne Reschedule oder ganz asynchron !!! + + if ( pDocument->IsInDdeLinkUpdate() ) + return; + +#if erDEBUGDOT + // set nDebug=1 in debugger to init things + if (nDebug == 1) + { + ++nDebug; + pDebugFile = fopen( aDebugDotFile, "a"); + if (!pDebugFile) + nDebug = 0; + else + nDebug = erDEBUGDOTRUN; + } + // set nDebug=3 (erDEBUGDOTRUN) in debugger to get any output + DebugElement aDebugElem( this); + // set nDebug=5 in debugger to close output + if (nDebug == 5) + { + nDebug = 0; + fclose( pDebugFile); + pDebugFile = 0; + } +#endif + + if (bRunning) + { + +#if erDEBUGDOT + if (!pDocument->GetRecursionHelper().IsDoingIteration() || + aDebugThisPos != aDebugLastPos) + erDEBUGDOT_OUT(aDebugThisPos == aDebugLastPos ? "orange" : + (pDocument->GetRecursionHelper().GetIteration() ? "blue" : + "red")); +#endif + + if (!pDocument->GetDocOptions().IsIter()) + { + aResult.SetResultError( errCircularReference ); + return; + } + + if (aResult.GetResultError() == errCircularReference) + aResult.SetResultError( 0 ); + + // Start or add to iteration list. + if (!pDocument->GetRecursionHelper().IsDoingIteration() || + !pDocument->GetRecursionHelper().GetRecursionInIterationStack().top()->bIsIterCell) + pDocument->GetRecursionHelper().SetInIterationReturn( true); + + return; + } + // #63038# no multiple interprets for GetErrCode, IsValue, GetValue and + // different entry point recursions. Would also lead to premature + // convergence in iterations. + if (pDocument->GetRecursionHelper().GetIteration() && nSeenInIteration == + pDocument->GetRecursionHelper().GetIteration()) + return ; + + erDEBUGDOT_OUT( pDocument->GetRecursionHelper().GetIteration() ? "magenta" : ""); + + ScRecursionHelper& rRecursionHelper = pDocument->GetRecursionHelper(); + BOOL bOldRunning = bRunning; + if (rRecursionHelper.GetRecursionCount() > MAXRECURSION) + { + bRunning = TRUE; + rRecursionHelper.SetInRecursionReturn( true); + } + else + { + InterpretTail( SCITP_NORMAL); + } + + // While leaving a recursion or iteration stack, insert its cells to the + // recursion list in reverse order. + if (rRecursionHelper.IsInReturn()) + { + if (rRecursionHelper.GetRecursionCount() > 0 || + !rRecursionHelper.IsDoingRecursion()) + rRecursionHelper.Insert( this, bOldRunning, aResult); + bool bIterationFromRecursion = false; + bool bResumeIteration = false; + do + { + if ((rRecursionHelper.IsInIterationReturn() && + rRecursionHelper.GetRecursionCount() == 0 && + !rRecursionHelper.IsDoingIteration()) || + bIterationFromRecursion || bResumeIteration) + { + ScFormulaCell* pIterCell = this; // scope for debug convenience + bool & rDone = rRecursionHelper.GetConvergingReference(); + rDone = false; + if (!bIterationFromRecursion && bResumeIteration) + { + bResumeIteration = false; + // Resuming iteration expands the range. + ScFormulaRecursionList::const_iterator aOldStart( + rRecursionHelper.GetLastIterationStart()); + rRecursionHelper.ResumeIteration(); + // Mark new cells being in iteration. + for (ScFormulaRecursionList::const_iterator aIter( + rRecursionHelper.GetIterationStart()); aIter != + aOldStart; ++aIter) + { + pIterCell = (*aIter).pCell; + pIterCell->bIsIterCell = TRUE; + } + // Mark older cells dirty again, in case they converted + // without accounting for all remaining cells in the circle + // that weren't touched so far, e.g. conditional. Restore + // backuped result. + USHORT nIteration = rRecursionHelper.GetIteration(); + for (ScFormulaRecursionList::const_iterator aIter( + aOldStart); aIter != + rRecursionHelper.GetIterationEnd(); ++aIter) + { + pIterCell = (*aIter).pCell; + if (pIterCell->nSeenInIteration == nIteration) + { + if (!pIterCell->bDirty || aIter == aOldStart) + { + pIterCell->aResult = (*aIter).aPreviousResult; + } + --pIterCell->nSeenInIteration; + } + pIterCell->bDirty = TRUE; + } + } + else + { + bResumeIteration = false; + // Close circle once. + rRecursionHelper.GetList().back().pCell->InterpretTail( + SCITP_CLOSE_ITERATION_CIRCLE); + // Start at 1, init things. + rRecursionHelper.StartIteration(); + // Mark all cells being in iteration. + for (ScFormulaRecursionList::const_iterator aIter( + rRecursionHelper.GetIterationStart()); aIter != + rRecursionHelper.GetIterationEnd(); ++aIter) + { + pIterCell = (*aIter).pCell; + pIterCell->bIsIterCell = TRUE; + } + } + bIterationFromRecursion = false; + USHORT nIterMax = pDocument->GetDocOptions().GetIterCount(); + for ( ; rRecursionHelper.GetIteration() <= nIterMax && !rDone; + rRecursionHelper.IncIteration()) + { + rDone = true; + for ( ScFormulaRecursionList::iterator aIter( + rRecursionHelper.GetIterationStart()); aIter != + rRecursionHelper.GetIterationEnd() && + !rRecursionHelper.IsInReturn(); ++aIter) + { + pIterCell = (*aIter).pCell; + if (pIterCell->IsDirtyOrInTableOpDirty() && + rRecursionHelper.GetIteration() != + pIterCell->GetSeenInIteration()) + { + (*aIter).aPreviousResult = pIterCell->aResult; + pIterCell->InterpretTail( SCITP_FROM_ITERATION); + } + rDone = rDone && !pIterCell->IsDirtyOrInTableOpDirty(); + } + if (rRecursionHelper.IsInReturn()) + { + bResumeIteration = true; + break; // for + // Don't increment iteration. + } + } + if (!bResumeIteration) + { + if (rDone) + { + for (ScFormulaRecursionList::const_iterator aIter( + rRecursionHelper.GetIterationStart()); + aIter != rRecursionHelper.GetIterationEnd(); + ++aIter) + { + pIterCell = (*aIter).pCell; + pIterCell->bIsIterCell = FALSE; + pIterCell->nSeenInIteration = 0; + pIterCell->bRunning = (*aIter).bOldRunning; + } + } + else + { + for (ScFormulaRecursionList::const_iterator aIter( + rRecursionHelper.GetIterationStart()); + aIter != rRecursionHelper.GetIterationEnd(); + ++aIter) + { + pIterCell = (*aIter).pCell; + pIterCell->bIsIterCell = FALSE; + pIterCell->nSeenInIteration = 0; + pIterCell->bRunning = (*aIter).bOldRunning; + // If one cell didn't converge, all cells of this + // circular dependency don't, no matter whether + // single cells did. + pIterCell->bDirty = FALSE; + pIterCell->bTableOpDirty = FALSE; + pIterCell->aResult.SetResultError( errNoConvergence); + pIterCell->bChanged = TRUE; + pIterCell->SetTextWidth( TEXTWIDTH_DIRTY); + pIterCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN); + } + } + // End this iteration and remove entries. + rRecursionHelper.EndIteration(); + bResumeIteration = rRecursionHelper.IsDoingIteration(); + } + } + if (rRecursionHelper.IsInRecursionReturn() && + rRecursionHelper.GetRecursionCount() == 0 && + !rRecursionHelper.IsDoingRecursion()) + { + bIterationFromRecursion = false; + // Iterate over cells known so far, start with the last cell + // encountered, inserting new cells if another recursion limit + // is reached. Repeat until solved. + rRecursionHelper.SetDoingRecursion( true); + do + { + rRecursionHelper.SetInRecursionReturn( false); + for (ScFormulaRecursionList::const_iterator aIter( + rRecursionHelper.GetStart()); + !rRecursionHelper.IsInReturn() && aIter != + rRecursionHelper.GetEnd(); ++aIter) + { + ScFormulaCell* pCell = (*aIter).pCell; + if (pCell->IsDirtyOrInTableOpDirty()) + { + pCell->InterpretTail( SCITP_NORMAL); + if (!pCell->IsDirtyOrInTableOpDirty() && !pCell->IsIterCell()) + pCell->bRunning = (*aIter).bOldRunning; + } + } + } while (rRecursionHelper.IsInRecursionReturn()); + rRecursionHelper.SetDoingRecursion( false); + if (rRecursionHelper.IsInIterationReturn()) + { + if (!bResumeIteration) + bIterationFromRecursion = true; + } + else if (bResumeIteration || + rRecursionHelper.IsDoingIteration()) + rRecursionHelper.GetList().erase( + rRecursionHelper.GetStart(), + rRecursionHelper.GetLastIterationStart()); + else + rRecursionHelper.Clear(); + } + } while (bIterationFromRecursion || bResumeIteration); + } +} + +void ScFormulaCell::InterpretTail( ScInterpretTailParameter eTailParam ) +{ + class RecursionCounter + { + ScRecursionHelper& rRec; + bool bStackedInIteration; + public: + RecursionCounter( ScRecursionHelper& r, ScFormulaCell* p ) : rRec(r) + { + bStackedInIteration = rRec.IsDoingIteration(); + if (bStackedInIteration) + rRec.GetRecursionInIterationStack().push( p); + rRec.IncRecursionCount(); + } + ~RecursionCounter() + { + rRec.DecRecursionCount(); + if (bStackedInIteration) + rRec.GetRecursionInIterationStack().pop(); + } + } aRecursionCounter( pDocument->GetRecursionHelper(), this); + nSeenInIteration = pDocument->GetRecursionHelper().GetIteration(); + if( !pCode->GetCodeLen() && !pCode->GetCodeError() ) + { + // #i11719# no UPN and no error and no token code but result string present + // => interpretation of this cell during name-compilation and unknown names + // => can't exchange underlying code array in CompileTokenArray() / + // Compile() because interpreter's token iterator would crash. + // This should only be a temporary condition and, since we set an + // error, if ran into it again we'd bump into the dirty-clearing + // condition further down. + if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) + { + pCode->SetCodeError( errNoCode ); + // This is worth an assertion; if encountered in daily work + // documents we might need another solution. Or just confirm correctness. + DBG_ERRORFILE( "ScFormulaCell::Interpret: no UPN, no error, no token, but string" ); + return; + } + CompileTokenArray(); + } + + if( pCode->GetCodeLen() && pDocument ) + { + class StackCleaner + { + ScDocument* pDoc; + ScInterpreter* pInt; + public: + StackCleaner( ScDocument* pD, ScInterpreter* pI ) + : pDoc(pD), pInt(pI) + {} + ~StackCleaner() + { + delete pInt; + pDoc->DecInterpretLevel(); + } + }; + pDocument->IncInterpretLevel(); + ScInterpreter* p = new ScInterpreter( this, pDocument, aPos, *pCode ); + StackCleaner aStackCleaner( pDocument, p); + USHORT nOldErrCode = aResult.GetResultError(); + if ( nSeenInIteration == 0 ) + { // Only the first time + // With bChanged=FALSE, if a newly compiled cell has a result of + // 0.0, no change is detected and the cell will not be repainted. + // bChanged = FALSE; + aResult.SetResultError( 0 ); + } + + switch ( aResult.GetResultError() ) + { + case errCircularReference : // will be determined again if so + aResult.SetResultError( 0 ); + break; + } + + BOOL bOldRunning = bRunning; + bRunning = TRUE; + p->Interpret(); + if (pDocument->GetRecursionHelper().IsInReturn() && eTailParam != SCITP_CLOSE_ITERATION_CIRCLE) + { + if (nSeenInIteration > 0) + --nSeenInIteration; // retry when iteration is resumed + return; + } + bRunning = bOldRunning; + + // #i102616# For single-sheet saving consider only content changes, not format type, + // because format type isn't set on loading (might be changed later) + BOOL bContentChanged = FALSE; + + // Do not create a HyperLink() cell if the formula results in an error. + if( p->GetError() && pCode->IsHyperLink()) + pCode->SetHyperLink(FALSE); + + if( p->GetError() && p->GetError() != errCircularReference) + { + bDirty = FALSE; + bTableOpDirty = FALSE; + bChanged = TRUE; + } + if (eTailParam == SCITP_FROM_ITERATION && IsDirtyOrInTableOpDirty()) + { + bool bIsValue = aResult.IsValue(); // the previous type + // Did it converge? + if ((bIsValue && p->GetResultType() == svDouble && fabs( + p->GetNumResult() - aResult.GetDouble()) <= + pDocument->GetDocOptions().GetIterEps()) || + (!bIsValue && p->GetResultType() == svString && + p->GetStringResult() == aResult.GetString())) + { + // A convergence in the first iteration doesn't necessarily + // mean that it's done, it may be because not all related cells + // of a circle changed their values yet. If the set really + // converges it will do so also during the next iteration. This + // fixes situations like of #i44115#. If this wasn't wanted an + // initial "uncalculated" value would be needed for all cells + // of a circular dependency => graph needed before calculation. + if (nSeenInIteration > 1 || + pDocument->GetDocOptions().GetIterCount() == 1) + { + bDirty = FALSE; + bTableOpDirty = FALSE; + } + } + } + + // New error code? + if( p->GetError() != nOldErrCode ) + { + bChanged = TRUE; + // bContentChanged only has to be set if the file content would be changed + if ( aResult.GetCellResultType() != svUnknown ) + bContentChanged = TRUE; + } + // Different number format? + if( nFormatType != p->GetRetFormatType() ) + { + nFormatType = p->GetRetFormatType(); + bChanged = TRUE; + } + if( nFormatIndex != p->GetRetFormatIndex() ) + { + nFormatIndex = p->GetRetFormatIndex(); + bChanged = TRUE; + } + + // In case of changes just obtain the result, no temporary and + // comparison needed anymore. + if (bChanged) + { + // #i102616# Compare anyway if the sheet is still marked unchanged for single-sheet saving + // Also handle special cases of initial results after loading. + + if ( !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) + { + ScFormulaResult aNewResult( p->GetResultToken()); + StackVar eOld = aResult.GetCellResultType(); + StackVar eNew = aNewResult.GetCellResultType(); + if ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) + { + // ScXMLTableRowCellContext::EndElement doesn't call SetFormulaResultDouble for 0 + // -> no change + } + else + { + if ( eOld == svHybridCell ) // string result from SetFormulaResultString? + eOld = svString; // ScHybridCellToken has a valid GetString method + + // #i106045# use approxEqual to compare with stored value + bContentChanged = (eOld != eNew || + (eNew == svDouble && !rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() )) || + (eNew == svString && aResult.GetString() != aNewResult.GetString())); + } + } + + aResult.SetToken( p->GetResultToken() ); + } + else + { + ScFormulaResult aNewResult( p->GetResultToken()); + StackVar eOld = aResult.GetCellResultType(); + StackVar eNew = aNewResult.GetCellResultType(); + bChanged = (eOld != eNew || + (eNew == svDouble && aResult.GetDouble() != aNewResult.GetDouble()) || + (eNew == svString && aResult.GetString() != aNewResult.GetString())); + + // #i102616# handle special cases of initial results after loading (only if the sheet is still marked unchanged) + if ( bChanged && !bContentChanged && pDocument->IsStreamValid(aPos.Tab()) ) + { + if ( ( eOld == svUnknown && ( eNew == svError || ( eNew == svDouble && aNewResult.GetDouble() == 0.0 ) ) ) || + ( eOld == svHybridCell && eNew == svString && aResult.GetString() == aNewResult.GetString() ) || + ( eOld == svDouble && eNew == svDouble && rtl::math::approxEqual( aResult.GetDouble(), aNewResult.GetDouble() ) ) ) + { + // no change, see above + } + else + bContentChanged = TRUE; + } + + aResult.Assign( aNewResult); + } + + // Precision as shown? + if ( aResult.IsValue() && !p->GetError() + && pDocument->GetDocOptions().IsCalcAsShown() + && nFormatType != NUMBERFORMAT_DATE + && nFormatType != NUMBERFORMAT_TIME + && nFormatType != NUMBERFORMAT_DATETIME ) + { + ULONG nFormat = pDocument->GetNumberFormat( aPos ); + if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + nFormat = nFormatIndex; + if ( (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + nFormat = ScGlobal::GetStandardFormat( + *pDocument->GetFormatTable(), nFormat, nFormatType ); + aResult.SetDouble( pDocument->RoundValueAsShown( + aResult.GetDouble(), nFormat)); + } + if (eTailParam == SCITP_NORMAL) + { + bDirty = FALSE; + bTableOpDirty = FALSE; + } + if( aResult.GetMatrix().Is() ) + { + // If the formula wasn't entered as a matrix formula, live on with + // the upper left corner and let reference counting delete the matrix. + if( cMatrixFlag != MM_FORMULA && !pCode->IsHyperLink() ) + aResult.SetToken( aResult.GetCellResultToken()); + } + if ( aResult.IsValue() && !::rtl::math::isFinite( aResult.GetDouble() ) ) + { + // Coded double error may occur via filter import. + USHORT nErr = GetDoubleErrorValue( aResult.GetDouble()); + aResult.SetResultError( nErr); + bChanged = bContentChanged = true; + } + if( bChanged ) + { + SetTextWidth( TEXTWIDTH_DIRTY ); + SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); + } + if (bContentChanged && pDocument->IsStreamValid(aPos.Tab())) + { + // pass bIgnoreLock=TRUE, because even if called from pending row height update, + // a changed result must still reset the stream flag + pDocument->SetStreamValid(aPos.Tab(), FALSE, TRUE); + } + if ( !pCode->IsRecalcModeAlways() ) + pDocument->RemoveFromFormulaTree( this ); + + // FORCED Zellen auch sofort auf Gueltigkeit testen (evtl. Makro starten) + + if ( pCode->IsRecalcModeForced() ) + { + ULONG nValidation = ((const SfxUInt32Item*) pDocument->GetAttr( + aPos.Col(), aPos.Row(), aPos.Tab(), ATTR_VALIDDATA ))->GetValue(); + if ( nValidation ) + { + const ScValidationData* pData = pDocument->GetValidationEntry( nValidation ); + if ( pData && !pData->IsDataValid( this, aPos ) ) + pData->DoCalcError( this ); + } + } + + // Reschedule verlangsamt das ganze erheblich, nur bei Prozentaenderung ausfuehren + ScProgress::GetInterpretProgress()->SetStateCountDownOnPercent( + pDocument->GetFormulaCodeInTree()/MIN_NO_CODES_PER_PROGRESS_UPDATE ); + } + else + { + // Zelle bei Compiler-Fehlern nicht ewig auf dirty stehenlassen + DBG_ASSERT( pCode->GetCodeError(), "kein UPN-Code und kein Fehler ?!?!" ); + bDirty = FALSE; + bTableOpDirty = FALSE; + } +} + + +void ScFormulaCell::SetMatColsRows( SCCOL nCols, SCROW nRows ) +{ + ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellTokenNonConst(); + if (pMat) + pMat->SetMatColsRows( nCols, nRows); + else if (nCols || nRows) + aResult.SetToken( new ScMatrixFormulaCellToken( nCols, nRows)); +} + + +void ScFormulaCell::GetMatColsRows( SCCOL & nCols, SCROW & nRows ) const +{ + const ScMatrixFormulaCellToken* pMat = aResult.GetMatrixFormulaCellToken(); + if (pMat) + pMat->GetMatColsRows( nCols, nRows); + else + { + nCols = 0; + nRows = 0; + } +} + + +ULONG ScFormulaCell::GetStandardFormat( SvNumberFormatter& rFormatter, ULONG nFormat ) const +{ + if ( nFormatIndex && (nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + return nFormatIndex; + //! not ScFormulaCell::IsValue(), that could reinterpret the formula again. + if ( aResult.IsValue() ) + return ScGlobal::GetStandardFormat( aResult.GetDouble(), rFormatter, nFormat, nFormatType ); + else + return ScGlobal::GetStandardFormat( rFormatter, nFormat, nFormatType ); +} + + +void __EXPORT ScFormulaCell::Notify( SvtBroadcaster&, const SfxHint& rHint) +{ + if ( !pDocument->IsInDtorClear() && !pDocument->GetHardRecalcState() ) + { + const ScHint* p = PTR_CAST( ScHint, &rHint ); + ULONG nHint = (p ? p->GetId() : 0); + if (nHint & (SC_HINT_DATACHANGED | SC_HINT_DYING | SC_HINT_TABLEOPDIRTY)) + { + BOOL bForceTrack = FALSE; + if ( nHint & SC_HINT_TABLEOPDIRTY ) + { + bForceTrack = !bTableOpDirty; + if ( !bTableOpDirty ) + { + pDocument->AddTableOpFormulaCell( this ); + bTableOpDirty = TRUE; + } + } + else + { + bForceTrack = !bDirty; + bDirty = TRUE; + } + // #35962# Don't remove from FormulaTree to put in FormulaTrack to + // put in FormulaTree again and again, only if necessary. + // Any other means except RECALCMODE_ALWAYS by which a cell could + // be in FormulaTree if it would notify other cells through + // FormulaTrack which weren't in FormulaTrack/FormulaTree before?!? + // #87866# Yes. The new TableOpDirty made it necessary to have a + // forced mode where formulas may still be in FormulaTree from + // TableOpDirty but have to notify dependents for normal dirty. + if ( (bForceTrack || !pDocument->IsInFormulaTree( this ) + || pCode->IsRecalcModeAlways()) + && !pDocument->IsInFormulaTrack( this ) ) + pDocument->AppendToFormulaTrack( this ); + } + } +} + +void ScFormulaCell::SetDirty() +{ + if ( !IsInChangeTrack() ) + { + if ( pDocument->GetHardRecalcState() ) + bDirty = TRUE; + else + { + // Mehrfach-FormulaTracking in Load und in CompileAll + // nach CopyScenario und CopyBlockFromClip vermeiden. + // Wenn unbedingtes FormulaTracking noetig, vor SetDirty bDirty=FALSE + // setzen, z.B. in CompileTokenArray + if ( !bDirty || !pDocument->IsInFormulaTree( this ) ) + { + bDirty = TRUE; + pDocument->AppendToFormulaTrack( this ); + pDocument->TrackFormulas(); + } + } + + if (pDocument->IsStreamValid(aPos.Tab())) + pDocument->SetStreamValid(aPos.Tab(), FALSE); + } +} + +void ScFormulaCell::SetDirtyAfterLoad() +{ + bDirty = TRUE; + if ( !pDocument->GetHardRecalcState() ) + pDocument->PutInFormulaTree( this ); +} + +void ScFormulaCell::SetTableOpDirty() +{ + if ( !IsInChangeTrack() ) + { + if ( pDocument->GetHardRecalcState() ) + bTableOpDirty = TRUE; + else + { + if ( !bTableOpDirty || !pDocument->IsInFormulaTree( this ) ) + { + if ( !bTableOpDirty ) + { + pDocument->AddTableOpFormulaCell( this ); + bTableOpDirty = TRUE; + } + pDocument->AppendToFormulaTrack( this ); + pDocument->TrackFormulas( SC_HINT_TABLEOPDIRTY ); + } + } + } +} + + +BOOL ScFormulaCell::IsDirtyOrInTableOpDirty() const +{ + return bDirty || (bTableOpDirty && pDocument->IsInInterpreterTableOp()); +} + + +void ScFormulaCell::SetErrCode( USHORT n ) +{ + /* FIXME: check the numerous places where ScTokenArray::GetCodeError() is + * used whether it is solely for transport of a simple result error and get + * rid of that abuse. */ + pCode->SetCodeError( n ); + // Hard set errors are transported as result type value per convention, + // e.g. via clipboard. ScFormulaResult::IsValue() and + // ScFormulaResult::GetDouble() handle that. + aResult.SetResultError( n ); +} + +void ScFormulaCell::AddRecalcMode( ScRecalcMode nBits ) +{ + if ( (nBits & RECALCMODE_EMASK) != RECALCMODE_NORMAL ) + bDirty = TRUE; + if ( nBits & RECALCMODE_ONLOAD_ONCE ) + { // OnLoadOnce nur zum Dirty setzen nach Filter-Import + nBits = (nBits & ~RECALCMODE_EMASK) | RECALCMODE_NORMAL; + } + pCode->AddRecalcMode( nBits ); +} + +// Dynamically create the URLField on a mouse-over action on a hyperlink() cell. +void ScFormulaCell::GetURLResult( String& rURL, String& rCellText ) +{ + String aCellString; + + Color* pColor; + + // Cell Text uses the Cell format while the URL uses + // the default format for the type. + ULONG nCellFormat = pDocument->GetNumberFormat( aPos ); + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + + if ( (nCellFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + nCellFormat = GetStandardFormat( *pFormatter,nCellFormat ); + + ULONG nURLFormat = ScGlobal::GetStandardFormat( *pFormatter,nCellFormat, NUMBERFORMAT_NUMBER); + + if ( IsValue() ) + { + double fValue = GetValue(); + pFormatter->GetOutputString( fValue, nCellFormat, rCellText, &pColor ); + } + else + { + GetString( aCellString ); + pFormatter->GetOutputString( aCellString, nCellFormat, rCellText, &pColor ); + } + ScConstMatrixRef xMat( aResult.GetMatrix()); + if (xMat) + { + ScMatValType nMatValType; + // determine if the matrix result is a string or value. + const ScMatrixValue* pMatVal = xMat->Get(0, 1, nMatValType); + if (pMatVal) + { + if (!ScMatrix::IsValueType( nMatValType)) + rURL = pMatVal->GetString(); + else + pFormatter->GetOutputString( pMatVal->fVal, nURLFormat, rURL, &pColor ); + } + } + + if(!rURL.Len()) + { + if(IsValue()) + pFormatter->GetOutputString( GetValue(), nURLFormat, rURL, &pColor ); + else + pFormatter->GetOutputString( aCellString, nURLFormat, rURL, &pColor ); + } +} + +bool ScFormulaCell::IsMultilineResult() +{ + if (!IsValue()) + return aResult.IsMultiline(); + return false; +} + +EditTextObject* ScFormulaCell::CreateURLObject() +{ + String aCellText; + String aURL; + GetURLResult( aURL, aCellText ); + + SvxURLField aUrlField( aURL, aCellText, SVXURLFORMAT_APPDEFAULT); + EditEngine& rEE = pDocument->GetEditEngine(); + rEE.SetText( EMPTY_STRING ); + rEE.QuickInsertField( SvxFieldItem( aUrlField, EE_FEATURE_FIELD ), ESelection( 0xFFFF, 0xFFFF ) ); + + return rEE.CreateTextObject(); +} + +// ============================================================================ + +ScDetectiveRefIter::ScDetectiveRefIter( ScFormulaCell* pCell ) +{ + pCode = pCell->GetCode(); + pCode->Reset(); + aPos = pCell->aPos; +} + +BOOL lcl_ScDetectiveRefIter_SkipRef( ScToken* p ) +{ + ScSingleRefData& rRef1 = p->GetSingleRef(); + if ( rRef1.IsColDeleted() || rRef1.IsRowDeleted() || rRef1.IsTabDeleted() + || !rRef1.Valid() ) + return TRUE; + if ( p->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; + if ( rRef2.IsColDeleted() || rRef2.IsRowDeleted() || rRef2.IsTabDeleted() + || !rRef2.Valid() ) + return TRUE; + } + return FALSE; +} + +BOOL ScDetectiveRefIter::GetNextRef( ScRange& rRange ) +{ + BOOL bRet = FALSE; + + ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + if (p) + p->CalcAbsIfRel( aPos ); + + while ( p && lcl_ScDetectiveRefIter_SkipRef( p ) ) + { + p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + if (p) + p->CalcAbsIfRel( aPos ); + } + + if( p ) + { + SingleDoubleRefProvider aProv( *p ); + rRange.aStart.Set( aProv.Ref1.nCol, aProv.Ref1.nRow, aProv.Ref1.nTab ); + rRange.aEnd.Set( aProv.Ref2.nCol, aProv.Ref2.nRow, aProv.Ref2.nTab ); + bRet = TRUE; + } + + return bRet; +} + +// ============================================================================ diff --git a/sc/source/core/data/cell2.cxx b/sc/source/core/data/cell2.cxx new file mode 100644 index 000000000000..acac874704c6 --- /dev/null +++ b/sc/source/core/data/cell2.cxx @@ -0,0 +1,1631 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: cell2.cxx,v $ + * $Revision: 1.34.102.2 $ + * + * 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 <algorithm> +#include <deque> + +#include <boost/bind.hpp> + +#include <vcl/mapmod.hxx> +#include <svx/editobj.hxx> +#include <svx/editstat.hxx> + +#include "cell.hxx" +#include "compiler.hxx" +#include "formula/errorcodes.hxx" +#include "document.hxx" +#include "rangenam.hxx" +#include "rechead.hxx" +#include "refupdat.hxx" +#include "scmatrix.hxx" +#include "editutil.hxx" +#include "chgtrack.hxx" +#include "externalrefmgr.hxx" + +using namespace formula; + +// STATIC DATA ----------------------------------------------------------- + +#ifdef USE_MEMPOOL +const USHORT nMemPoolEditCell = (0x1000 - 64) / sizeof(ScNoteCell); +IMPL_FIXEDMEMPOOL_NEWDEL( ScEditCell, nMemPoolEditCell, nMemPoolEditCell ) +#endif + +// ============================================================================ + +ScEditCell::ScEditCell( const EditTextObject* pObject, ScDocument* pDocP, + const SfxItemPool* pFromPool ) : + ScBaseCell( CELLTYPE_EDIT ), + pString( NULL ), + pDoc( pDocP ) +{ + SetTextObject( pObject, pFromPool ); +} + +ScEditCell::ScEditCell( const ScEditCell& rCell, ScDocument& rDoc ) : + ScBaseCell( rCell ), + pString( NULL ), + pDoc( &rDoc ) +{ + SetTextObject( rCell.pData, rCell.pDoc->GetEditPool() ); +} + +ScEditCell::ScEditCell( const String& rString, ScDocument* pDocP ) : + ScBaseCell( CELLTYPE_EDIT ), + pString( NULL ), + pDoc( pDocP ) +{ + DBG_ASSERT( rString.Search('\n') != STRING_NOTFOUND || + rString.Search(CHAR_CR) != STRING_NOTFOUND, + "EditCell mit einfachem Text !?!?" ); + + EditEngine& rEngine = pDoc->GetEditEngine(); + rEngine.SetText( rString ); + pData = rEngine.CreateTextObject(); +} + +ScEditCell::~ScEditCell() +{ + delete pData; + delete pString; + +#ifdef DBG_UTIL + eCellType = CELLTYPE_DESTROYED; +#endif +} + +void ScEditCell::SetData( const EditTextObject* pObject, + const SfxItemPool* pFromPool ) +{ + if ( pString ) + { + delete pString; + pString = NULL; + } + delete pData; + SetTextObject( pObject, pFromPool ); +} + +void ScEditCell::GetData( const EditTextObject*& rpObject ) const +{ + rpObject = pData; +} + +void ScEditCell::GetString( String& rString ) const +{ + if ( pString ) + rString = *pString; + else if ( pData ) + { + // auch Text von URL-Feldern, Doc-Engine ist eine ScFieldEditEngine + EditEngine& rEngine = pDoc->GetEditEngine(); + rEngine.SetText( *pData ); + rString = ScEditUtil::GetMultilineString(rEngine); // string with line separators between paragraphs + // cache short strings for formulas + if ( rString.Len() < 256 ) + ((ScEditCell*)this)->pString = new String( rString ); //! non-const + } + else + rString.Erase(); +} + +void ScEditCell::SetTextObject( const EditTextObject* pObject, + const SfxItemPool* pFromPool ) +{ + if ( pObject ) + { + if ( pFromPool && pDoc->GetEditPool() == pFromPool ) + pData = pObject->Clone(); + else + { //! anderer Pool + // Leider gibt es keinen anderen Weg, um den Pool umzuhaengen, + // als das Object durch eine entsprechende Engine zu schleusen.. + EditEngine& rEngine = pDoc->GetEditEngine(); + if ( pObject->HasOnlineSpellErrors() ) + { + ULONG nControl = rEngine.GetControlWord(); + const ULONG nSpellControl = EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS; + BOOL bNewControl = ( (nControl & nSpellControl) != nSpellControl ); + if ( bNewControl ) + rEngine.SetControlWord( nControl | nSpellControl ); + rEngine.SetText( *pObject ); + pData = rEngine.CreateTextObject(); + if ( bNewControl ) + rEngine.SetControlWord( nControl ); + } + else + { + rEngine.SetText( *pObject ); + pData = rEngine.CreateTextObject(); + } + } + } + else + pData = NULL; +} + +// ============================================================================ + +namespace +{ + +using std::deque; + +typedef SCCOLROW(*DimensionSelector)(const ScSingleRefData&); + + +static SCCOLROW lcl_GetCol(const ScSingleRefData& rData) +{ + return rData.nCol; +} + + +static SCCOLROW lcl_GetRow(const ScSingleRefData& rData) +{ + return rData.nRow; +} + + +static SCCOLROW lcl_GetTab(const ScSingleRefData& rData) +{ + return rData.nTab; +} + + +/** Check if both references span the same range in selected dimension. + */ +static bool +lcl_checkRangeDimension( + const SingleDoubleRefProvider& rRef1, + const SingleDoubleRefProvider& rRef2, + const DimensionSelector aWhich) +{ + return + aWhich(rRef1.Ref1) == aWhich(rRef2.Ref1) + && aWhich(rRef1.Ref2) == aWhich(rRef2.Ref2); +} + + +static bool +lcl_checkRangeDimensions( + const SingleDoubleRefProvider& rRef1, + const SingleDoubleRefProvider& rRef2, + bool& bCol, bool& bRow, bool& bTab) +{ + const bool bSameCols(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetCol)); + const bool bSameRows(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetRow)); + const bool bSameTabs(lcl_checkRangeDimension(rRef1, rRef2, lcl_GetTab)); + + // Test if exactly two dimensions are equal + if (!(bSameCols ^ bSameRows ^ bSameTabs) + && (bSameCols || bSameRows || bSameTabs)) + { + bCol = !bSameCols; + bRow = !bSameRows; + bTab = !bSameTabs; + return true; + } + return false; +} + + +/** Check if references in given reference list can possibly + form a range. To do that, two of their dimensions must be the same. + */ +static bool +lcl_checkRangeDimensions( + const deque<ScToken*>::const_iterator aBegin, + const deque<ScToken*>::const_iterator aEnd, + bool& bCol, bool& bRow, bool& bTab) +{ + deque<ScToken*>::const_iterator aCur(aBegin); + ++aCur; + const SingleDoubleRefProvider aRef(**aBegin); + bool bOk(false); + { + const SingleDoubleRefProvider aRefCur(**aCur); + bOk = lcl_checkRangeDimensions(aRef, aRefCur, bCol, bRow, bTab); + } + while (bOk && aCur != aEnd) + { + const SingleDoubleRefProvider aRefCur(**aCur); + bool bColTmp(false); + bool bRowTmp(false); + bool bTabTmp(false); + bOk = lcl_checkRangeDimensions(aRef, aRefCur, bColTmp, bRowTmp, bTabTmp); + bOk = bOk && (bCol == bColTmp && bRow == bRowTmp && bTab == bTabTmp); + ++aCur; + } + + if (bOk && aCur == aEnd) + { + bCol = bCol; + bRow = bRow; + bTab = bTab; + return true; + } + return false; +} + + +bool +lcl_lessReferenceBy( + const ScToken* const pRef1, const ScToken* const pRef2, + const DimensionSelector aWhich) +{ + const SingleDoubleRefProvider rRef1(*pRef1); + const SingleDoubleRefProvider rRef2(*pRef2); + return aWhich(rRef1.Ref1) < aWhich(rRef2.Ref1); +} + + +/** Returns true if range denoted by token pRef2 starts immediately after + range denoted by token pRef1. Dimension, in which the comparison takes + place, is given by aWhich. + */ +bool +lcl_isImmediatelyFollowing( + const ScToken* const pRef1, const ScToken* const pRef2, + const DimensionSelector aWhich) +{ + const SingleDoubleRefProvider rRef1(*pRef1); + const SingleDoubleRefProvider rRef2(*pRef2); + return aWhich(rRef2.Ref1) - aWhich(rRef1.Ref2) == 1; +} + + +static bool +lcl_checkIfAdjacent( + const deque<ScToken*>& rReferences, + const DimensionSelector aWhich) +{ + typedef deque<ScToken*>::const_iterator Iter; + Iter aBegin(rReferences.begin()); + Iter aEnd(rReferences.end()); + Iter aBegin1(aBegin); + ++aBegin1, --aEnd; + return std::equal( + aBegin, aEnd, aBegin1, + boost::bind(lcl_isImmediatelyFollowing, _1, _2, aWhich)); +} + + +static void +lcl_fillRangeFromRefList( + const deque<ScToken*>& rReferences, ScRange& rRange) +{ + const ScSingleRefData aStart( + SingleDoubleRefProvider(*rReferences.front()).Ref1); + rRange.aStart.Set(aStart.nCol, aStart.nRow, aStart.nTab); + const ScSingleRefData aEnd( + SingleDoubleRefProvider(*rReferences.back()).Ref2); + rRange.aEnd.Set(aEnd.nCol, aEnd.nRow, aEnd.nTab); +} + + +static bool +lcl_refListFormsOneRange( + const ScAddress& aPos, deque<ScToken*>& rReferences, + ScRange& rRange) +{ + std::for_each( + rReferences.begin(), rReferences.end(), + bind(&ScToken::CalcAbsIfRel, _1, aPos)) + ; + if (rReferences.size() == 1) { + lcl_fillRangeFromRefList(rReferences, rRange); + return true; + } + + bool bCell(false); + bool bRow(false); + bool bTab(false); + if (lcl_checkRangeDimensions(rReferences.begin(), rReferences.end(), + bCell, bRow, bTab)) + { + DimensionSelector aWhich; + if (bCell) + { + aWhich = lcl_GetCol; + } + else if (bRow) + { + aWhich = lcl_GetRow; + } + else if (bTab) + { + aWhich = lcl_GetTab; + } + else + { + OSL_ENSURE(false, "lcl_checkRangeDimensions shouldn't allow that!"); + aWhich = lcl_GetRow; // initialize to avoid warning + } + // Sort the references by start of range + std::sort(rReferences.begin(), rReferences.end(), + boost::bind(lcl_lessReferenceBy, _1, _2, aWhich)); + if (lcl_checkIfAdjacent(rReferences, aWhich)) + { + lcl_fillRangeFromRefList(rReferences, rRange); + return true; + } + } + return false; +} + + +bool lcl_isReference(const FormulaToken& rToken) +{ + return + rToken.GetType() == svSingleRef || + rToken.GetType() == svDoubleRef; +} + +} + +BOOL ScFormulaCell::IsEmpty() +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + return aResult.GetCellResultType() == formula::svEmptyCell; +} + +BOOL ScFormulaCell::IsEmptyDisplayedAsString() +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + return aResult.IsEmptyDisplayedAsString(); +} + +BOOL ScFormulaCell::IsValue() +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + return aResult.IsValue(); +} + +double ScFormulaCell::GetValue() +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && + !aResult.GetResultError()) + return aResult.GetDouble(); + return 0.0; +} + +double ScFormulaCell::GetValueAlways() +{ + // for goal seek: return result value even if error code is set + + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + return aResult.GetDouble(); +} + +void ScFormulaCell::GetString( String& rString ) +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + if ((!pCode->GetCodeError() || pCode->GetCodeError() == errDoubleRef) && + !aResult.GetResultError()) + rString = aResult.GetString(); + else + rString.Erase(); +} + +const ScMatrix* ScFormulaCell::GetMatrix() +{ + if ( pDocument->GetAutoCalc() ) + { + // Was stored !bDirty but an accompanying matrix cell was bDirty? + // => we need to get the matrix. + if (!bDirty && cMatrixFlag == MM_FORMULA && !aResult.GetMatrix().Is()) + bDirty = TRUE; + if ( IsDirtyOrInTableOpDirty() ) + Interpret(); + } + return aResult.GetMatrix(); +} + +BOOL ScFormulaCell::GetMatrixOrigin( ScAddress& rPos ) const +{ + switch ( cMatrixFlag ) + { + case MM_FORMULA : + rPos = aPos; + return TRUE; +// break; + case MM_REFERENCE : + { + pCode->Reset(); + ScToken* t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + if( t ) + { + ScSingleRefData& rRef = t->GetSingleRef(); + rRef.CalcAbsIfRel( aPos ); + if ( rRef.Valid() ) + { + rPos.Set( rRef.nCol, rRef.nRow, rRef.nTab ); + return TRUE; + } + } + } + break; + } + return FALSE; +} + + +/* + Edge-Values: + + 8 + 4 16 + 2 + + innerhalb: 1 + ausserhalb: 0 + (reserviert: offen: 32) + */ + +USHORT ScFormulaCell::GetMatrixEdge( ScAddress& rOrgPos ) +{ + switch ( cMatrixFlag ) + { + case MM_FORMULA : + case MM_REFERENCE : + { + static SCCOL nC; + static SCROW nR; + ScAddress aOrg; + if ( !GetMatrixOrigin( aOrg ) ) + return 0; // dumm gelaufen.. + if ( aOrg != rOrgPos ) + { // erstes Mal oder andere Matrix als letztes Mal + rOrgPos = aOrg; + ScFormulaCell* pFCell; + if ( cMatrixFlag == MM_REFERENCE ) + pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg ); + else + pFCell = this; // this MM_FORMULA + // this gibt's nur einmal, kein Vergleich auf pFCell==this + if ( pFCell && pFCell->GetCellType() == CELLTYPE_FORMULA + && pFCell->cMatrixFlag == MM_FORMULA ) + { + pFCell->GetMatColsRows( nC, nR ); + if ( nC == 0 || nR == 0 ) + { // aus altem Dokument geladen, neu erzeugen + nC = 1; + nR = 1; + ScAddress aTmpOrg; + ScBaseCell* pCell; + ScAddress aAdr( aOrg ); + aAdr.IncCol(); + BOOL bCont = TRUE; + do + { + pCell = pDocument->GetCell( aAdr ); + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE + && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) + { + nC++; + aAdr.IncCol(); + } + else + bCont = FALSE; + } while ( bCont ); + aAdr = aOrg; + aAdr.IncRow(); + bCont = TRUE; + do + { + pCell = pDocument->GetCell( aAdr ); + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->cMatrixFlag == MM_REFERENCE + && GetMatrixOrigin( aTmpOrg ) && aTmpOrg == aOrg ) + { + nR++; + aAdr.IncRow(); + } + else + bCont = FALSE; + } while ( bCont ); + pFCell->SetMatColsRows( nC, nR ); + } + } + else + { +#ifndef PRODUCT + String aTmp; + ByteString aMsg( "broken Matrix, no MatFormula at origin, Pos: " ); + aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); + aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); + aMsg += ", MatOrg: "; + aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); + aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); + DBG_ERRORFILE( aMsg.GetBuffer() ); +#endif + return 0; // bad luck ... + } + } + // here we are, healthy and clean, somewhere in between + SCsCOL dC = aPos.Col() - aOrg.Col(); + SCsROW dR = aPos.Row() - aOrg.Row(); + USHORT nEdges = 0; + if ( dC >= 0 && dR >= 0 && dC < nC && dR < nR ) + { + if ( dC == 0 ) + nEdges |= 4; // linke Kante + if ( dC+1 == nC ) + nEdges |= 16; // rechte Kante + if ( dR == 0 ) + nEdges |= 8; // obere Kante + if ( dR+1 == nR ) + nEdges |= 2; // untere Kante + if ( !nEdges ) + nEdges = 1; // mittendrin + } +#ifndef PRODUCT + else + { + String aTmp; + ByteString aMsg( "broken Matrix, Pos: " ); + aPos.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); + aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); + aMsg += ", MatOrg: "; + aOrg.Format( aTmp, SCA_VALID_COL | SCA_VALID_ROW, pDocument ); + aMsg += ByteString( aTmp, RTL_TEXTENCODING_ASCII_US ); + aMsg += ", MatCols: "; + aMsg += ByteString::CreateFromInt32( nC ); + aMsg += ", MatRows: "; + aMsg += ByteString::CreateFromInt32( nR ); + aMsg += ", DiffCols: "; + aMsg += ByteString::CreateFromInt32( dC ); + aMsg += ", DiffRows: "; + aMsg += ByteString::CreateFromInt32( dR ); + DBG_ERRORFILE( aMsg.GetBuffer() ); + } +#endif + return nEdges; +// break; + } + default: + return 0; + } +} + +USHORT ScFormulaCell::GetErrCode() +{ + if (IsDirtyOrInTableOpDirty() && pDocument->GetAutoCalc()) + Interpret(); + /* FIXME: If ScTokenArray::SetCodeError() was really only for code errors + * and not also abused for signaling other error conditions we could bail + * out even before attempting to interpret broken code. */ + USHORT nErr = pCode->GetCodeError(); + if (nErr) + return nErr; + return aResult.GetResultError(); +} + +USHORT ScFormulaCell::GetRawError() +{ + USHORT nErr = pCode->GetCodeError(); + if (nErr) + return nErr; + return aResult.GetResultError(); +} + +BOOL ScFormulaCell::HasOneReference( ScRange& r ) const +{ + pCode->Reset(); + ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + if( p && !pCode->GetNextReferenceRPN() ) // nur eine! + { + p->CalcAbsIfRel( aPos ); + SingleDoubleRefProvider aProv( *p ); + r.aStart.Set( aProv.Ref1.nCol, + aProv.Ref1.nRow, + aProv.Ref1.nTab ); + r.aEnd.Set( aProv.Ref2.nCol, + aProv.Ref2.nRow, + aProv.Ref2.nTab ); + return TRUE; + } + else + return FALSE; +} + +bool +ScFormulaCell::HasRefListExpressibleAsOneReference(ScRange& rRange) const +{ + /* If there appears just one reference in the formula, it's the same + as HasOneReference(). If there are more of them, they can denote + one range if they are (sole) arguments of one function. + Union of these references must form one range and their + intersection must be empty set. + */ + pCode->Reset(); + // Get first reference, if any + ScToken* const pFirstReference( + dynamic_cast<ScToken*>(pCode->GetNextReferenceRPN())); + if (pFirstReference) + { + // Collect all consecutive references, starting by the one + // already found + std::deque<ScToken*> aReferences; + aReferences.push_back(pFirstReference); + FormulaToken* pToken(pCode->NextRPN()); + FormulaToken* pFunction(0); + while (pToken) + { + if (lcl_isReference(*pToken)) + { + aReferences.push_back(dynamic_cast<ScToken*>(pToken)); + pToken = pCode->NextRPN(); + } + else + { + if (pToken->IsFunction()) + { + pFunction = pToken; + } + break; + } + } + if (pFunction && !pCode->GetNextReferenceRPN() + && (pFunction->GetParamCount() == aReferences.size())) + { + return lcl_refListFormsOneRange(aPos, aReferences, rRange); + } + } + return false; +} + +BOOL ScFormulaCell::HasRelNameReference() const +{ + pCode->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pCode->GetNextReferenceRPN()) ) != NULL ) + { + if ( t->GetSingleRef().IsRelName() || + (t->GetType() == formula::svDoubleRef && + t->GetDoubleRef().Ref2.IsRelName()) ) + return TRUE; + } + return FALSE; +} + +BOOL ScFormulaCell::HasColRowName() const +{ + pCode->Reset(); + return (pCode->GetNextColRowName() != NULL); +} + +void ScFormulaCell::UpdateReference(UpdateRefMode eUpdateRefMode, + const ScRange& r, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScDocument* pUndoDoc, const ScAddress* pUndoCellPos ) +{ + SCCOL nCol1 = r.aStart.Col(); + SCROW nRow1 = r.aStart.Row(); + SCTAB nTab1 = r.aStart.Tab(); + SCCOL nCol2 = r.aEnd.Col(); + SCROW nRow2 = r.aEnd.Row(); + SCTAB nTab2 = r.aEnd.Tab(); + SCCOL nCol = aPos.Col(); + SCROW nRow = aPos.Row(); + SCTAB nTab = aPos.Tab(); + ScAddress aUndoPos( aPos ); // position for undo cell in pUndoDoc + if ( pUndoCellPos ) + aUndoPos = *pUndoCellPos; + ScAddress aOldPos( aPos ); +// BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde + BOOL bIsInsert = FALSE; + if (eUpdateRefMode == URM_INSDEL) + { + bIsInsert = (nDx >= 0 && nDy >= 0 && nDz >= 0); + if ( nDx && nRow >= nRow1 && nRow <= nRow2 && + nTab >= nTab1 && nTab <= nTab2 ) + { + if (nCol >= nCol1) + { + nCol = sal::static_int_cast<SCCOL>( nCol + nDx ); + if ((SCsCOL) nCol < 0) + nCol = 0; + else if ( nCol > MAXCOL ) + nCol = MAXCOL; + aPos.SetCol( nCol ); +// bPosChanged = TRUE; + } + } + if ( nDy && nCol >= nCol1 && nCol <= nCol2 && + nTab >= nTab1 && nTab <= nTab2 ) + { + if (nRow >= nRow1) + { + nRow = sal::static_int_cast<SCROW>( nRow + nDy ); + if ((SCsROW) nRow < 0) + nRow = 0; + else if ( nRow > MAXROW ) + nRow = MAXROW; + aPos.SetRow( nRow ); +// bPosChanged = TRUE; + } + } + if ( nDz && nCol >= nCol1 && nCol <= nCol2 && + nRow >= nRow1 && nRow <= nRow2 ) + { + if (nTab >= nTab1) + { + SCTAB nMaxTab = pDocument->GetTableCount() - 1; + nTab = sal::static_int_cast<SCTAB>( nTab + nDz ); + if ((SCsTAB) nTab < 0) + nTab = 0; + else if ( nTab > nMaxTab ) + nTab = nMaxTab; + aPos.SetTab( nTab ); +// bPosChanged = TRUE; + } + } + } + else if ( r.In( aPos ) ) + { + aOldPos.Set( nCol - nDx, nRow - nDy, nTab - nDz ); +// bPosChanged = TRUE; + } + + BOOL bHasRefs = FALSE; + BOOL bHasColRowNames = FALSE; + BOOL bOnRefMove = FALSE; + if ( !pDocument->IsClipOrUndo() ) + { + pCode->Reset(); + bHasRefs = (pCode->GetNextReferenceRPN() != NULL); + if ( !bHasRefs || eUpdateRefMode == URM_COPY ) + { + pCode->Reset(); + bHasColRowNames = (pCode->GetNextColRowName() != NULL); + bHasRefs = bHasRefs || bHasColRowNames; + } + bOnRefMove = pCode->IsRecalcModeOnRefMove(); + } + if( bHasRefs || bOnRefMove ) + { + ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; + BOOL bValChanged; + ScRangeData* pRangeData; + BOOL bRangeModified; // any range, not only shared formula + BOOL bRefSizeChanged; + if ( bHasRefs ) + { + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + pRangeData = aComp.UpdateReference(eUpdateRefMode, aOldPos, r, + nDx, nDy, nDz, + bValChanged, bRefSizeChanged); + bRangeModified = aComp.HasModifiedRange(); + } + else + { + bValChanged = FALSE; + pRangeData = NULL; + bRangeModified = FALSE; + bRefSizeChanged = FALSE; + } + if ( bOnRefMove ) + bOnRefMove = (bValChanged || (aPos != aOldPos)); + // Cell may reference itself, e.g. ocColumn, ocRow without parameter + + BOOL bColRowNameCompile, bHasRelName, bNewListening, bInDeleteUndo; + if ( bHasRefs ) + { + // Upon Insert ColRowNames have to be recompiled in case the + // insertion occurs right in front of the range. + bColRowNameCompile = + (eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0)); + if ( bColRowNameCompile ) + { + bColRowNameCompile = FALSE; + ScToken* t; + ScRangePairList* pColList = pDocument->GetColNameRanges(); + ScRangePairList* pRowList = pDocument->GetRowNameRanges(); + pCode->Reset(); + while ( !bColRowNameCompile && (t = static_cast<ScToken*>(pCode->GetNextColRowName())) != NULL ) + { + ScSingleRefData& rRef = t->GetSingleRef(); + if ( nDy > 0 && rRef.IsColRel() ) + { // ColName + rRef.CalcAbsIfRel( aPos ); + ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); + ScRangePair* pR = pColList->Find( aAdr ); + if ( pR ) + { // definiert + if ( pR->GetRange(1).aStart.Row() == nRow1 ) + bColRowNameCompile = TRUE; + } + else + { // on the fly + if ( rRef.nRow + 1 == nRow1 ) + bColRowNameCompile = TRUE; + } + } + if ( nDx > 0 && rRef.IsRowRel() ) + { // RowName + rRef.CalcAbsIfRel( aPos ); + ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); + ScRangePair* pR = pRowList->Find( aAdr ); + if ( pR ) + { // definiert + if ( pR->GetRange(1).aStart.Col() == nCol1 ) + bColRowNameCompile = TRUE; + } + else + { // on the fly + if ( rRef.nCol + 1 == nCol1 ) + bColRowNameCompile = TRUE; + } + } + } + } + else if ( eUpdateRefMode == URM_MOVE ) + { // bei Move/D&D neu kompilieren wenn ColRowName verschoben wurde + // oder diese Zelle auf einen zeigt und verschoben wurde + bColRowNameCompile = bCompile; // evtl. aus Copy-ctor + if ( !bColRowNameCompile ) + { + BOOL bMoved = (aPos != aOldPos); + pCode->Reset(); + ScToken* t = static_cast<ScToken*>(pCode->GetNextColRowName()); + if ( t && bMoved ) + bColRowNameCompile = TRUE; + while ( t && !bColRowNameCompile ) + { + ScSingleRefData& rRef = t->GetSingleRef(); + rRef.CalcAbsIfRel( aPos ); + if ( rRef.Valid() ) + { + ScAddress aAdr( rRef.nCol, rRef.nRow, rRef.nTab ); + if ( r.In( aAdr ) ) + bColRowNameCompile = TRUE; + } + t = static_cast<ScToken*>(pCode->GetNextColRowName()); + } + } + } + else if ( eUpdateRefMode == URM_COPY && bHasColRowNames && bValChanged ) + { + bColRowNameCompile = TRUE; + } + ScChangeTrack* pChangeTrack = pDocument->GetChangeTrack(); + if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) + bInDeleteUndo = TRUE; + else + bInDeleteUndo = FALSE; + // RelNameRefs are always moved + bHasRelName = HasRelNameReference(); + // Reference changed and new listening needed? + // Except in Insert/Delete without specialties. + bNewListening = (bRangeModified || pRangeData || bColRowNameCompile + || (bValChanged && (eUpdateRefMode != URM_INSDEL || + bInDeleteUndo || bRefSizeChanged)) || + (bHasRelName && eUpdateRefMode != URM_COPY)) + // #i36299# Don't duplicate action during cut&paste / drag&drop + // on a cell in the range moved, start/end listeners is done + // via ScDocument::DeleteArea() and ScDocument::CopyFromClip(). + && !(eUpdateRefMode == URM_MOVE && + pDocument->IsInsertingFromOtherDoc() && r.In(aPos)); + if ( bNewListening ) + EndListeningTo( pDocument, pOld, aOldPos ); + } + else + { + bColRowNameCompile = bHasRelName = bNewListening = bInDeleteUndo = + FALSE; + } + + BOOL bNeedDirty; + // NeedDirty bei Aenderungen ausser Copy und Move/Insert ohne RelNames + if ( bRangeModified || pRangeData || bColRowNameCompile || + (bValChanged && eUpdateRefMode != URM_COPY && + (eUpdateRefMode != URM_MOVE || bHasRelName) && + (!bIsInsert || bHasRelName || bInDeleteUndo || + bRefSizeChanged)) || bOnRefMove) + bNeedDirty = TRUE; + else + bNeedDirty = FALSE; + if (pUndoDoc && (bValChanged || pRangeData || bOnRefMove)) + { + // Copy the cell to aUndoPos, which is its current position in the document, + // so this works when UpdateReference is called before moving the cells + // (InsertCells/DeleteCells - aPos is changed above) as well as when UpdateReference + // is called after moving the cells (MoveBlock/PasteFromClip - aOldPos is changed). + + // If there is already a formula cell in the undo document, don't overwrite it, + // the first (oldest) is the important cell. + if ( pUndoDoc->GetCellType( aUndoPos ) != CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aUndoPos, + pOld, eTempGrammar, cMatrixFlag ); + pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) + pUndoDoc->PutCell( aUndoPos, pFCell ); + } + } + bValChanged = FALSE; + if ( pRangeData ) + { // Replace shared formula with own formula + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = pRangeData->GetCode()->Clone(); + ScCompiler aComp2(pDocument, aPos, *pCode); + aComp2.SetGrammar(pDocument->GetGrammar()); + aComp2.UpdateSharedFormulaReference( eUpdateRefMode, aOldPos, r, + nDx, nDy, nDz ); + bValChanged = TRUE; + bNeedDirty = TRUE; + } + if ( ( bCompile = (bCompile || bValChanged || bRangeModified || bColRowNameCompile) ) != 0 ) + { + CompileTokenArray( bNewListening ); // kein Listening + bNeedDirty = TRUE; + } + if ( !bInDeleteUndo ) + { // In ChangeTrack Delete-Reject listeners are established in + // InsertCol/InsertRow + if ( bNewListening ) + { + if ( eUpdateRefMode == URM_INSDEL ) + { + // Inserts/Deletes re-establish listeners after all + // UpdateReference calls. + // All replaced shared formula listeners have to be + // established after an Insert or Delete. Do nothing here. + SetNeedsListening( TRUE); + } + else + StartListeningTo( pDocument ); + } + } + if ( bNeedDirty && (!(eUpdateRefMode == URM_INSDEL && bHasRelName) || pRangeData) ) + { // Referenzen abgeschnitten, ungueltig o.ae.? + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + // kein Interpret in SubMinimalRecalc wegen evtl. falscher Referenzen + pDocument->SetAutoCalc( FALSE ); + SetDirty(); + pDocument->SetAutoCalc( bOldAutoCalc ); + } + + delete pOld; + } + + pCode->Reset(); + for ( formula::FormulaToken* t = pCode->GetNextReferenceOrName(); t; t = pCode->GetNextReferenceOrName() ) + { + StackVar sv = t->GetType(); + if (sv == svExternalSingleRef || sv == svExternalDoubleRef || sv == svExternalName) + { + pDocument->GetExternalRefManager()->updateRefCell(aOldPos, aPos, eUpdateRefMode == URM_COPY); + break; + } + } +} + +void ScFormulaCell::UpdateInsertTab(SCTAB nTable) +{ + BOOL bPosChanged = ( aPos.Tab() >= nTable ? TRUE : FALSE ); + pCode->Reset(); + if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) + { + EndListeningTo( pDocument ); + // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateInsertTab ! + if ( bPosChanged ) + aPos.IncTab(); + ScRangeData* pRangeData; + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + pRangeData = aComp.UpdateInsertTab( nTable, FALSE ); + if (pRangeData) // Shared Formula gegen echte Formel + { // austauschen + BOOL bRefChanged; + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = new ScTokenArray( *pRangeData->GetCode() ); + ScCompiler aComp2(pDocument, aPos, *pCode); + aComp2.SetGrammar(pDocument->GetGrammar()); + aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); + aComp2.UpdateInsertTab( nTable, FALSE ); + // If the shared formula contained a named range/formula containing + // an absolute reference to a sheet, those have to be readjusted. + aComp2.UpdateDeleteTab( nTable, FALSE, TRUE, bRefChanged ); + bCompile = TRUE; + } + // kein StartListeningTo weil pTab[nTab] noch nicht existiert! + } + else if ( bPosChanged ) + aPos.IncTab(); +} + +BOOL ScFormulaCell::UpdateDeleteTab(SCTAB nTable, BOOL bIsMove) +{ + BOOL bRefChanged = FALSE; + BOOL bPosChanged = ( aPos.Tab() > nTable ? TRUE : FALSE ); + pCode->Reset(); + if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) + { + EndListeningTo( pDocument ); + // IncTab _nach_ EndListeningTo und _vor_ Compiler UpdateDeleteTab ! + if ( bPosChanged ) + aPos.IncTab(-1); + ScRangeData* pRangeData; + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + pRangeData = aComp.UpdateDeleteTab(nTable, bIsMove, FALSE, bRefChanged); + if (pRangeData) // Shared Formula gegen echte Formel + { // austauschen + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = pRangeData->GetCode()->Clone(); + ScCompiler aComp2(pDocument, aPos, *pCode); + aComp2.SetGrammar(pDocument->GetGrammar()); + aComp2.CompileTokenArray(); + aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); + aComp2.UpdateDeleteTab( nTable, FALSE, FALSE, bRefChanged ); + // If the shared formula contained a named range/formula containing + // an absolute reference to a sheet, those have to be readjusted. + aComp2.UpdateInsertTab( nTable,TRUE ); + // bRefChanged kann beim letzten UpdateDeleteTab zurueckgesetzt worden sein + bRefChanged = TRUE; + bCompile = TRUE; + } + // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! + } + else if ( bPosChanged ) + aPos.IncTab(-1); + + return bRefChanged; +} + +void ScFormulaCell::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) +{ + pCode->Reset(); + if( pCode->GetNextReferenceRPN() && !pDocument->IsClipOrUndo() ) + { + EndListeningTo( pDocument ); + // SetTab _nach_ EndListeningTo und _vor_ Compiler UpdateMoveTab ! + aPos.SetTab( nTabNo ); + ScRangeData* pRangeData; + ScCompiler aComp(pDocument, aPos, *pCode); + aComp.SetGrammar(pDocument->GetGrammar()); + pRangeData = aComp.UpdateMoveTab( nOldPos, nNewPos, FALSE ); + if (pRangeData) // Shared Formula gegen echte Formel + { // austauschen + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = pRangeData->GetCode()->Clone(); + ScCompiler aComp2(pDocument, aPos, *pCode); + aComp2.SetGrammar(pDocument->GetGrammar()); + aComp2.CompileTokenArray(); + aComp2.MoveRelWrap(pRangeData->GetMaxCol(), pRangeData->GetMaxRow()); + aComp2.UpdateMoveTab( nOldPos, nNewPos, TRUE ); + bCompile = TRUE; + } + // kein StartListeningTo weil pTab[nTab] noch nicht korrekt! + } + else + aPos.SetTab( nTabNo ); +} + +void ScFormulaCell::UpdateInsertTabAbs(SCTAB nTable) +{ + if( !pDocument->IsClipOrUndo() ) + { + pCode->Reset(); + ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + while( p ) + { + ScSingleRefData& rRef1 = p->GetSingleRef(); + if( !rRef1.IsTabRel() && (SCsTAB) nTable <= rRef1.nTab ) + rRef1.nTab++; + if( p->GetType() == formula::svDoubleRef ) + { + ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; + if( !rRef2.IsTabRel() && (SCsTAB) nTable <= rRef2.nTab ) + rRef2.nTab++; + } + p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + } + } +} + +BOOL ScFormulaCell::TestTabRefAbs(SCTAB nTable) +{ + BOOL bRet = FALSE; + if( !pDocument->IsClipOrUndo() ) + { + pCode->Reset(); + ScToken* p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + while( p ) + { + ScSingleRefData& rRef1 = p->GetSingleRef(); + if( !rRef1.IsTabRel() ) + { + if( (SCsTAB) nTable != rRef1.nTab ) + bRet = TRUE; + else if (nTable != aPos.Tab()) + rRef1.nTab = aPos.Tab(); + } + if( p->GetType() == formula::svDoubleRef ) + { + ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; + if( !rRef2.IsTabRel() ) + { + if( (SCsTAB) nTable != rRef2.nTab ) + bRet = TRUE; + else if (nTable != aPos.Tab()) + rRef2.nTab = aPos.Tab(); + } + } + p = static_cast<ScToken*>(pCode->GetNextReferenceRPN()); + } + } + return bRet; +} + +void ScFormulaCell::UpdateCompile( BOOL bForceIfNameInUse ) +{ + if ( bForceIfNameInUse && !bCompile ) + bCompile = pCode->HasNameOrColRowName(); + if ( bCompile ) + pCode->SetCodeError( 0 ); // make sure it will really be compiled + CompileTokenArray(); +} + +// Referenzen transponieren - wird nur in Clipboard-Dokumenten aufgerufen + +void ScFormulaCell::TransposeReference() +{ + BOOL bFound = FALSE; + pCode->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pCode->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsColRel() && rRef1.IsRowRel() ) + { + BOOL bDouble = (t->GetType() == formula::svDoubleRef); + ScSingleRefData& rRef2 = (bDouble ? t->GetDoubleRef().Ref2 : rRef1); + if ( !bDouble || (rRef2.IsColRel() && rRef2.IsRowRel()) ) + { + INT16 nTemp; + + nTemp = rRef1.nRelCol; + rRef1.nRelCol = static_cast<SCCOL>(rRef1.nRelRow); + rRef1.nRelRow = static_cast<SCROW>(nTemp); + + if ( bDouble ) + { + nTemp = rRef2.nRelCol; + rRef2.nRelCol = static_cast<SCCOL>(rRef2.nRelRow); + rRef2.nRelRow = static_cast<SCROW>(nTemp); + } + + bFound = TRUE; + } + } + } + + if (bFound) + bCompile = TRUE; +} + +void ScFormulaCell::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, + ScDocument* pUndoDoc ) +{ + EndListeningTo( pDocument ); + + ScAddress aOldPos = aPos; + BOOL bPosChanged = FALSE; // ob diese Zelle bewegt wurde + + ScRange aDestRange( rDest, ScAddress( + static_cast<SCCOL>(rDest.Col() + rSource.aEnd.Row() - rSource.aStart.Row()), + static_cast<SCROW>(rDest.Row() + rSource.aEnd.Col() - rSource.aStart.Col()), + rDest.Tab() + rSource.aEnd.Tab() - rSource.aStart.Tab() ) ); + if ( aDestRange.In( aOldPos ) ) + { + // Position zurueckrechnen + SCsCOL nRelPosX = aOldPos.Col(); + SCsROW nRelPosY = aOldPos.Row(); + SCsTAB nRelPosZ = aOldPos.Tab(); + ScRefUpdate::DoTranspose( nRelPosX, nRelPosY, nRelPosZ, pDocument, aDestRange, rSource.aStart ); + aOldPos.Set( nRelPosX, nRelPosY, nRelPosZ ); + bPosChanged = TRUE; + } + + ScTokenArray* pOld = pUndoDoc ? pCode->Clone() : NULL; + BOOL bRefChanged = FALSE; + ScToken* t; + + ScRangeData* pShared = NULL; + pCode->Reset(); + while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) + { + if( t->GetOpCode() == ocName ) + { + ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); + if (pName) + { + if (pName->IsModified()) + bRefChanged = TRUE; + if (pName->HasType(RT_SHAREDMOD)) + pShared = pName; + } + } + else if( t->GetType() != svIndex ) + { + t->CalcAbsIfRel( aOldPos ); + BOOL bMod; + { // own scope for SingleDoubleRefModifier dtor if SingleRef + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, + rDest, rRef ) != UR_NOTHING || bPosChanged); + } + if ( bMod ) + { + t->CalcRelFromAbs( aPos ); + bRefChanged = TRUE; + } + } + } + + if (pShared) // Shared Formula gegen echte Formel austauschen + { + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = new ScTokenArray( *pShared->GetCode() ); + bRefChanged = TRUE; + pCode->Reset(); + while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) + { + if( t->GetType() != svIndex ) + { + t->CalcAbsIfRel( aOldPos ); + BOOL bMod; + { // own scope for SingleDoubleRefModifier dtor if SingleRef + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + bMod = (ScRefUpdate::UpdateTranspose( pDocument, rSource, + rDest, rRef ) != UR_NOTHING || bPosChanged); + } + if ( bMod ) + t->CalcRelFromAbs( aPos ); + } + } + } + + if (bRefChanged) + { + if (pUndoDoc) + { + ScFormulaCell* pFCell = new ScFormulaCell( pUndoDoc, aPos, pOld, + eTempGrammar, cMatrixFlag); + pFCell->aResult.SetToken( NULL); // to recognize it as changed later (Cut/Paste!) + pUndoDoc->PutCell( aPos.Col(), aPos.Row(), aPos.Tab(), pFCell ); + } + + bCompile = TRUE; + CompileTokenArray(); // ruft auch StartListeningTo + SetDirty(); + } + else + StartListeningTo( pDocument ); // Listener wie vorher + + delete pOld; +} + +void ScFormulaCell::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + EndListeningTo( pDocument ); + + BOOL bRefChanged = FALSE; + ScToken* t; + ScRangeData* pShared = NULL; + + pCode->Reset(); + while( (t = static_cast<ScToken*>(pCode->GetNextReferenceOrName())) != NULL ) + { + if( t->GetOpCode() == ocName ) + { + ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); + if (pName) + { + if (pName->IsModified()) + bRefChanged = TRUE; + if (pName->HasType(RT_SHAREDMOD)) + pShared = pName; + } + } + else if( t->GetType() != svIndex ) + { + t->CalcAbsIfRel( aPos ); + BOOL bMod; + { // own scope for SingleDoubleRefModifier dtor if SingleRef + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, + rRef ) != UR_NOTHING); + } + if ( bMod ) + { + t->CalcRelFromAbs( aPos ); + bRefChanged = TRUE; + } + } + } + + if (pShared) // Shared Formula gegen echte Formel austauschen + { + pDocument->RemoveFromFormulaTree( this ); // update formula count + delete pCode; + pCode = new ScTokenArray( *pShared->GetCode() ); + bRefChanged = TRUE; + pCode->Reset(); + while( (t = static_cast<ScToken*>(pCode->GetNextReference())) != NULL ) + { + if( t->GetType() != svIndex ) + { + t->CalcAbsIfRel( aPos ); + BOOL bMod; + { // own scope for SingleDoubleRefModifier dtor if SingleRef + SingleDoubleRefModifier aMod( *t ); + ScComplexRefData& rRef = aMod.Ref(); + bMod = (ScRefUpdate::UpdateGrow( rArea,nGrowX,nGrowY, + rRef ) != UR_NOTHING); + } + if ( bMod ) + t->CalcRelFromAbs( aPos ); + } + } + } + + if (bRefChanged) + { + bCompile = TRUE; + CompileTokenArray(); // ruft auch StartListeningTo + SetDirty(); + } + else + StartListeningTo( pDocument ); // Listener wie vorher +} + +BOOL lcl_IsRangeNameInUse(USHORT nIndex, ScTokenArray* pCode, ScRangeName* pNames) +{ + for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) + { + if (p->GetOpCode() == ocName) + { + if (p->GetIndex() == nIndex) + return TRUE; + else + { + // RangeData kann Null sein in bestimmten Excel-Dateien (#31168#) + ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); + if (pSubName && lcl_IsRangeNameInUse(nIndex, + pSubName->GetCode(), pNames)) + return TRUE; + } + } + } + return FALSE; +} + +BOOL ScFormulaCell::IsRangeNameInUse(USHORT nIndex) const +{ + return lcl_IsRangeNameInUse( nIndex, pCode, pDocument->GetRangeName() ); +} + +void lcl_FindRangeNamesInUse(std::set<USHORT>& rIndexes, ScTokenArray* pCode, ScRangeName* pNames) +{ + for (FormulaToken* p = pCode->First(); p; p = pCode->Next()) + { + if (p->GetOpCode() == ocName) + { + USHORT nTokenIndex = p->GetIndex(); + rIndexes.insert( nTokenIndex ); + + ScRangeData* pSubName = pNames->FindIndex(p->GetIndex()); + if (pSubName) + lcl_FindRangeNamesInUse(rIndexes, pSubName->GetCode(), pNames); + } + } +} + +void ScFormulaCell::FindRangeNamesInUse(std::set<USHORT>& rIndexes) const +{ + lcl_FindRangeNamesInUse( rIndexes, pCode, pDocument->GetRangeName() ); +} + +void ScFormulaCell::ReplaceRangeNamesInUse( const ScRangeData::IndexMap& rMap ) +{ + for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) + { + if( p->GetOpCode() == ocName ) + { + sal_uInt16 nIndex = p->GetIndex(); + ScRangeData::IndexMap::const_iterator itr = rMap.find(nIndex); + sal_uInt16 nNewIndex = itr == rMap.end() ? nIndex : itr->second; + if ( nIndex != nNewIndex ) + { + p->SetIndex( nNewIndex ); + bCompile = TRUE; + } + } + } + if( bCompile ) + CompileTokenArray(); +} + +void ScFormulaCell::CompileDBFormula() +{ + for( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) + { + if ( p->GetOpCode() == ocDBArea + || (p->GetOpCode() == ocName && p->GetIndex() >= SC_START_INDEX_DB_COLL) ) + { + bCompile = TRUE; + CompileTokenArray(); + SetDirty(); + break; + } + } +} + +void ScFormulaCell::CompileDBFormula( BOOL bCreateFormulaString ) +{ + // zwei Phasen, muessen (!) nacheinander aufgerufen werden: + // 1. FormelString mit alten Namen erzeugen + // 2. FormelString mit neuen Namen kompilieren + if ( bCreateFormulaString ) + { + BOOL bRecompile = FALSE; + pCode->Reset(); + for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) + { + switch ( p->GetOpCode() ) + { + case ocBad: // DB-Bereich evtl. zugefuegt + case ocColRowName: // #36762# falls Namensgleichheit + case ocDBArea: // DB-Bereich + bRecompile = TRUE; + break; + case ocName: + if ( p->GetIndex() >= SC_START_INDEX_DB_COLL ) + bRecompile = TRUE; // DB-Bereich + break; + default: + ; // nothing + } + } + if ( bRecompile ) + { + String aFormula; + GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); + if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) + { + if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) + aFormula.Erase( aFormula.Len()-1 , 1 ); + if ( aFormula.GetChar(0) == '{' ) + aFormula.Erase( 0, 1 ); + } + EndListeningTo( pDocument ); + pDocument->RemoveFromFormulaTree( this ); + pCode->Clear(); + SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); + } + } + else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) + { + Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar ); + aResult.SetToken( NULL); + SetDirty(); + } +} + +void ScFormulaCell::CompileNameFormula( BOOL bCreateFormulaString ) +{ + // zwei Phasen, muessen (!) nacheinander aufgerufen werden: + // 1. FormelString mit alten RangeNames erzeugen + // 2. FormelString mit neuen RangeNames kompilieren + if ( bCreateFormulaString ) + { + BOOL bRecompile = FALSE; + pCode->Reset(); + for ( FormulaToken* p = pCode->First(); p && !bRecompile; p = pCode->Next() ) + { + switch ( p->GetOpCode() ) + { + case ocBad: // RangeName evtl. zugefuegt + case ocColRowName: // #36762# falls Namensgleichheit + bRecompile = TRUE; + break; + default: + if ( p->GetType() == svIndex ) + bRecompile = TRUE; // RangeName + } + } + if ( bRecompile ) + { + String aFormula; + GetFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); + if ( GetMatrixFlag() != MM_NONE && aFormula.Len() ) + { + if ( aFormula.GetChar( aFormula.Len()-1 ) == '}' ) + aFormula.Erase( aFormula.Len()-1 , 1 ); + if ( aFormula.GetChar(0) == '{' ) + aFormula.Erase( 0, 1 ); + } + EndListeningTo( pDocument ); + pDocument->RemoveFromFormulaTree( this ); + pCode->Clear(); + SetHybridFormula( aFormula, formula::FormulaGrammar::GRAM_NATIVE); + } + } + else if ( !pCode->GetLen() && aResult.GetHybridFormula().Len() ) + { + Compile( aResult.GetHybridFormula(), FALSE, eTempGrammar ); + aResult.SetToken( NULL); + SetDirty(); + } +} + +void ScFormulaCell::CompileColRowNameFormula() +{ + pCode->Reset(); + for ( FormulaToken* p = pCode->First(); p; p = pCode->Next() ) + { + if ( p->GetOpCode() == ocColRowName ) + { + bCompile = TRUE; + CompileTokenArray(); + SetDirty(); + break; + } + } +} + +// ============================================================================ + diff --git a/sc/source/core/data/clipparam.cxx b/sc/source/core/data/clipparam.cxx new file mode 100644 index 000000000000..b2ec555a0d5c --- /dev/null +++ b/sc/source/core/data/clipparam.cxx @@ -0,0 +1,203 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: document.cxx,v $ + * $Revision: 1.90.36.8 $ + * + * 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 "clipparam.hxx" + +using ::std::vector; + +ScClipParam::ScClipParam() : + meDirection(Unspecified), + mbCutMode(false) +{ +} + +ScClipParam::ScClipParam(const ScRange& rRange, bool bCutMode) : + meDirection(Unspecified), + mbCutMode(bCutMode) +{ + maRanges.Append(rRange); +} + +ScClipParam::ScClipParam(const ScClipParam& r) : + maRanges(r.maRanges), + meDirection(r.meDirection), + mbCutMode(r.mbCutMode) +{ +} + +bool ScClipParam::isMultiRange() const +{ + return maRanges.Count() > 1; +} + +SCCOL ScClipParam::getPasteColSize() +{ + if (!maRanges.Count()) + return 0; + + switch (meDirection) + { + case ScClipParam::Column: + { + SCCOL nColSize = 0; + for (ScRangePtr p = maRanges.First(); p; p = maRanges.Next()) + nColSize += p->aEnd.Col() - p->aStart.Col() + 1; + return nColSize; + } + case ScClipParam::Row: + { + // We assume that all ranges have identical column size. + const ScRange& rRange = *maRanges.First(); + return rRange.aEnd.Col() - rRange.aStart.Col() + 1; + } + case ScClipParam::Unspecified: + default: + ; + } + return 0; +} + +SCROW ScClipParam::getPasteRowSize() +{ + if (!maRanges.Count()) + return 0; + + switch (meDirection) + { + case ScClipParam::Column: + { + // We assume that all ranges have identical row size. + const ScRange& rRange = *maRanges.First(); + return rRange.aEnd.Row() - rRange.aStart.Row() + 1; + } + case ScClipParam::Row: + { + SCROW nRowSize = 0; + for (ScRangePtr p = maRanges.First(); p; p = maRanges.Next()) + nRowSize += p->aEnd.Row() - p->aStart.Row() + 1; + return nRowSize; + } + case ScClipParam::Unspecified: + default: + ; + } + return 0; +} + +ScRange ScClipParam::getWholeRange() const +{ + ScRange aWhole; + bool bFirst = true; + ScRangeList aRanges = maRanges; + for (ScRange* p = aRanges.First(); p; p = aRanges.Next()) + { + if (bFirst) + { + aWhole = *p; + bFirst = false; + continue; + } + + if (aWhole.aStart.Col() > p->aStart.Col()) + aWhole.aStart.SetCol(p->aStart.Col()); + + if (aWhole.aStart.Row() > p->aStart.Row()) + aWhole.aStart.SetRow(p->aStart.Row()); + + if (aWhole.aEnd.Col() < p->aEnd.Col()) + aWhole.aEnd.SetCol(p->aEnd.Col()); + + if (aWhole.aEnd.Row() < p->aEnd.Row()) + aWhole.aEnd.SetRow(p->aEnd.Row()); + } + return aWhole; +} + +void ScClipParam::transpose() +{ + switch (meDirection) + { + case Column: + meDirection = ScClipParam::Row; + break; + case Row: + meDirection = ScClipParam::Column; + break; + case Unspecified: + default: + ; + } + + ScRangeList aNewRanges; + if (maRanges.Count()) + { + ScRange* p = maRanges.First(); + SCCOL nColOrigin = p->aStart.Col(); + SCROW nRowOrigin = p->aStart.Row(); + for (; p; p = maRanges.Next()) + { + SCCOL nColDelta = p->aStart.Col() - nColOrigin; + SCROW nRowDelta = p->aStart.Row() - nRowOrigin; + SCCOL nCol1 = 0; + SCCOL nCol2 = static_cast<SCCOL>(p->aEnd.Row() - p->aStart.Row()); + SCROW nRow1 = 0; + SCROW nRow2 = static_cast<SCROW>(p->aEnd.Col() - p->aStart.Col()); + nCol1 += static_cast<SCCOL>(nRowDelta); + nCol2 += static_cast<SCCOL>(nRowDelta); + nRow1 += static_cast<SCROW>(nColDelta); + nRow2 += static_cast<SCROW>(nColDelta); + ScRange aNew(nCol1, nRow1, p->aStart.Tab(), nCol2, nRow2, p->aStart.Tab()); + aNewRanges.Append(aNew); + } + } + maRanges = aNewRanges; +} + +// ============================================================================ + +ScClipRangeNameData::ScClipRangeNameData() : + mbReplace(false) +{ +} + +ScClipRangeNameData::~ScClipRangeNameData() +{ +} + +void ScClipRangeNameData::insert(sal_uInt16 nOldIndex, sal_uInt16 nNewIndex) +{ + maRangeMap.insert( + ScRangeData::IndexMap::value_type(nOldIndex, nNewIndex)); +} diff --git a/sc/source/core/data/column.cxx b/sc/source/core/data/column.cxx new file mode 100644 index 000000000000..afcd809eed1d --- /dev/null +++ b/sc/source/core/data/column.cxx @@ -0,0 +1,2195 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: column.cxx,v $ + * $Revision: 1.31.128.9 $ + * + * 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 <map> + +#include <svtools/poolcach.hxx> +#include <svtools/zforlist.hxx> +#include <svx/scripttypeitem.hxx> +#include <string.h> + +#include "scitems.hxx" +#include "column.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "attarray.hxx" +#include "patattr.hxx" +#include "compiler.hxx" +#include "brdcst.hxx" +#include "markdata.hxx" +#include "detfunc.hxx" // for Notes in Sort/Swap +#include "postit.hxx" + +//#pragma optimize ( "", off ) +// nur Search ohne Optimierung! + +// STATIC DATA ----------------------------------------------------------- +using namespace formula; + +inline BOOL IsAmbiguousScriptNonZero( BYTE nScript ) +{ + //! move to a header file + return ( nScript != SCRIPTTYPE_LATIN && + nScript != SCRIPTTYPE_ASIAN && + nScript != SCRIPTTYPE_COMPLEX && + nScript != 0 ); +} + +// ----------------------------------------------------------------------------------------- + + +ScColumn::ScColumn() : + nCol( 0 ), + nCount( 0 ), + nLimit( 0 ), + pItems( NULL ), + pAttrArray( NULL ), + pDocument( NULL ) +{ +} + + +ScColumn::~ScColumn() +{ + FreeAll(); + if (pAttrArray) delete pAttrArray; +} + + +void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument* pDoc) +{ + nCol = nNewCol; + nTab = nNewTab; + pDocument = pDoc; + pAttrArray = new ScAttrArray( nCol, nTab, pDocument ); +} + + +SCsROW ScColumn::GetNextUnprotected( SCROW nRow, BOOL bUp ) const +{ + return pAttrArray->GetNextUnprotected(nRow, bUp); +} + + +USHORT ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, USHORT nMask ) const +{ + // nix:0, mitte:1, unten:2, links:4, oben:8, rechts:16, offen:32 + if ( !pItems ) + return 0; + if ( nRow1 == nRow2 ) + { + SCSIZE nIndex; + if ( Search( nRow1, nIndex ) ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) + { + ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); + return ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); + } + } + return 0; + } + else + { + ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); + BOOL bOpen = FALSE; + USHORT nEdges = 0; + SCSIZE nIndex; + Search( nRow1, nIndex ); + while ( nIndex < nCount && pItems[nIndex].nRow <= nRow2 ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) + { + nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); + if ( nEdges ) + { + if ( nEdges & 8 ) + bOpen = TRUE; // obere Kante oeffnet, weitersehen + else if ( !bOpen ) + return nEdges | 32; // es gibt was, was nicht geoeffnet wurde + else if ( nEdges & 1 ) + return nEdges; // mittendrin + // (nMask & 16 und (4 und nicht 16)) oder + // (nMask & 4 und (16 und nicht 4)) + if ( ((nMask & 16) && (nEdges & 4) && !(nEdges & 16)) + || ((nMask & 4) && (nEdges & 16) && !(nEdges & 4)) ) + return nEdges; // nur linke/rechte Kante + if ( nEdges & 2 ) + bOpen = FALSE; // untere Kante schliesst + } + } + nIndex++; + } + if ( bOpen ) + nEdges |= 32; // es geht noch weiter + return nEdges; + } +} + + +BOOL ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark) const +{ + if ( rMark.IsMultiMarked() ) + { + BOOL bFound = FALSE; + + ScAddress aOrg( ScAddress::INITIALIZE_INVALID ); + ScAddress aCurOrg( ScAddress::INITIALIZE_INVALID ); + SCROW nTop, nBottom; + ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol ); + while ( !bFound && aMarkIter.Next( nTop, nBottom ) ) + { + BOOL bOpen = FALSE; + USHORT nEdges; + SCSIZE nIndex; + Search( nTop, nIndex ); + while ( !bFound && nIndex < nCount && pItems[nIndex].nRow <= nBottom ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->GetMatrixFlag() ) + { + nEdges = ((ScFormulaCell*)pCell)->GetMatrixEdge( aOrg ); + if ( nEdges ) + { + if ( nEdges & 8 ) + bOpen = TRUE; // obere Kante oeffnet, weitersehen + else if ( !bOpen ) + return TRUE; // es gibt was, was nicht geoeffnet wurde + else if ( nEdges & 1 ) + bFound = TRUE; // mittendrin, alles selektiert? + // (4 und nicht 16) oder (16 und nicht 4) + if ( (((nEdges & 4) | 16) ^ ((nEdges & 16) | 4)) ) + bFound = TRUE; // nur linke/rechte Kante, alles selektiert? + if ( nEdges & 2 ) + bOpen = FALSE; // untere Kante schliesst + + if ( bFound ) + { // alles selektiert? + if ( aCurOrg != aOrg ) + { // neue Matrix zu pruefen? + aCurOrg = aOrg; + ScFormulaCell* pFCell; + if ( ((ScFormulaCell*)pCell)->GetMatrixFlag() + == MM_REFERENCE ) + pFCell = (ScFormulaCell*) pDocument->GetCell( aOrg ); + else + pFCell = (ScFormulaCell*)pCell; + SCCOL nC; + SCROW nR; + pFCell->GetMatColsRows( nC, nR ); + ScRange aRange( aOrg, ScAddress( + aOrg.Col() + nC - 1, aOrg.Row() + nR - 1, + aOrg.Tab() ) ); + if ( rMark.IsAllMarked( aRange ) ) + bFound = FALSE; + } + else + bFound = FALSE; // war schon + } + } + } + nIndex++; + } + if ( bOpen ) + return TRUE; + } + return bFound; + } + else + return FALSE; +} + + +//UNUSED2009-05 BOOL ScColumn::HasLines( SCROW nRow1, SCROW nRow2, Rectangle& rSizes, +//UNUSED2009-05 BOOL bLeft, BOOL bRight ) const +//UNUSED2009-05 { +//UNUSED2009-05 return pAttrArray->HasLines( nRow1, nRow2, rSizes, bLeft, bRight ); +//UNUSED2009-05 } + + +BOOL ScColumn::HasAttrib( SCROW nRow1, SCROW nRow2, USHORT nMask ) const +{ + return pAttrArray->HasAttrib( nRow1, nRow2, nMask ); +} + + +BOOL ScColumn::HasAttribSelection( const ScMarkData& rMark, USHORT nMask ) const +{ + BOOL bFound = FALSE; + + SCROW nTop; + SCROW nBottom; + + if (rMark.IsMultiMarked()) + { + ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol ); + while (aMarkIter.Next( nTop, nBottom ) && !bFound) + { + if (pAttrArray->HasAttrib( nTop, nBottom, nMask )) + bFound = TRUE; + } + } + + return bFound; +} + + +BOOL ScColumn::ExtendMerge( SCCOL nThisCol, SCROW nStartRow, SCROW nEndRow, + SCCOL& rPaintCol, SCROW& rPaintRow, + BOOL bRefresh, BOOL bAttrs ) +{ + return pAttrArray->ExtendMerge( nThisCol, nStartRow, nEndRow, rPaintCol, rPaintRow, bRefresh, bAttrs ); +} + + +void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, BOOL bDeep ) const +{ + SCROW nTop; + SCROW nBottom; + + if ( rMark.IsMultiMarked() ) + { + const ScMarkArray* pArray = rMark.GetArray() + nCol; + if ( pArray->HasMarks() ) + { + ScMarkArrayIter aMarkIter( pArray ); + while (aMarkIter.Next( nTop, nBottom )) + pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep ); + } + } +} + + +void ScColumn::MergePatternArea( ScMergePatternState& rState, SCROW nRow1, SCROW nRow2, BOOL bDeep ) const +{ + pAttrArray->MergePatternArea( nRow1, nRow2, rState, bDeep ); +} + + +void ScColumn::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, + ScLineFlags& rFlags, + SCROW nStartRow, SCROW nEndRow, BOOL bLeft, SCCOL nDistRight ) const +{ + pAttrArray->MergeBlockFrame( pLineOuter, pLineInner, rFlags, nStartRow, nEndRow, bLeft, nDistRight ); +} + + +void ScColumn::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner, + SCROW nStartRow, SCROW nEndRow, BOOL bLeft, SCCOL nDistRight ) +{ + pAttrArray->ApplyBlockFrame( pLineOuter, pLineInner, nStartRow, nEndRow, bLeft, nDistRight ); +} + + +const ScPatternAttr* ScColumn::GetPattern( SCROW nRow ) const +{ + return pAttrArray->GetPattern( nRow ); +} + + +const SfxPoolItem* ScColumn::GetAttr( SCROW nRow, USHORT nWhich ) const +{ + return &pAttrArray->GetPattern( nRow )->GetItemSet().Get(nWhich); +} + + +const ScPatternAttr* ScColumn::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const +{ + ::std::map< const ScPatternAttr*, size_t > aAttrMap; + const ScPatternAttr* pMaxPattern = 0; + size_t nMaxCount = 0; + + ScAttrIterator aAttrIter( pAttrArray, nStartRow, nEndRow ); + const ScPatternAttr* pPattern; + SCROW nAttrRow1 = 0, nAttrRow2 = 0; + + while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != 0 ) + { + size_t& rnCount = aAttrMap[ pPattern ]; + rnCount += (nAttrRow2 - nAttrRow1 + 1); + if( rnCount > nMaxCount ) + { + pMaxPattern = pPattern; + nMaxCount = rnCount; + } + } + + return pMaxPattern; +} + + +ULONG ScColumn::GetNumberFormat( SCROW nRow ) const +{ + return pAttrArray->GetPattern( nRow )->GetNumberFormat( pDocument->GetFormatTable() ); +} + + +SCsROW ScColumn::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark ) +{ + SCROW nTop = 0; + SCROW nBottom = 0; + BOOL bFound = FALSE; + + if ( rMark.IsMultiMarked() ) + { + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + while (aMarkIter.Next( nTop, nBottom )) + { + pAttrArray->ApplyCacheArea( nTop, nBottom, pCache ); + bFound = TRUE; + } + } + + if (!bFound) + return -1; + else if (nTop==0 && nBottom==MAXROW) + return 0; + else + return nBottom; +} + + +void ScColumn::ChangeSelectionIndent( BOOL bIncrement, const ScMarkData& rMark ) +{ + SCROW nTop; + SCROW nBottom; + + if ( pAttrArray && rMark.IsMultiMarked() ) + { + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + while (aMarkIter.Next( nTop, nBottom )) + pAttrArray->ChangeIndent(nTop, nBottom, bIncrement); + } +} + + +void ScColumn::ClearSelectionItems( const USHORT* pWhich,const ScMarkData& rMark ) +{ + SCROW nTop; + SCROW nBottom; + + if ( pAttrArray && rMark.IsMultiMarked() ) + { + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + while (aMarkIter.Next( nTop, nBottom )) + pAttrArray->ClearItems(nTop, nBottom, pWhich); + } +} + + +void ScColumn::DeleteSelection( USHORT nDelFlag, const ScMarkData& rMark ) +{ + SCROW nTop; + SCROW nBottom; + + if ( rMark.IsMultiMarked() ) + { + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + while (aMarkIter.Next( nTop, nBottom )) + DeleteArea(nTop, nBottom, nDelFlag); + } +} + + +void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr ) +{ + const SfxItemSet* pSet = &rPatAttr.GetItemSet(); + SfxItemPoolCache aCache( pDocument->GetPool(), pSet ); + + const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow ); + + // TRUE = alten Eintrag behalten + + ScPatternAttr* pNewPattern = (ScPatternAttr*) &aCache.ApplyTo( *pPattern, TRUE ); + ScDocumentPool::CheckRef( *pPattern ); + ScDocumentPool::CheckRef( *pNewPattern ); + + if (pNewPattern != pPattern) + pAttrArray->SetPattern( nRow, pNewPattern ); +} + + +void ScColumn::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr ) +{ + const SfxItemSet* pSet = &rPatAttr.GetItemSet(); + SfxItemPoolCache aCache( pDocument->GetPool(), pSet ); + pAttrArray->ApplyCacheArea( nStartRow, nEndRow, &aCache ); +} + + +void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange, + const ScPatternAttr& rPattern, short nNewType ) +{ + const SfxItemSet* pSet = &rPattern.GetItemSet(); + SfxItemPoolCache aCache( pDocument->GetPool(), pSet ); + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + SCROW nEndRow = rRange.aEnd.Row(); + for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ ) + { + SCROW nRow1, nRow2; + const ScPatternAttr* pPattern = pAttrArray->GetPatternRange( + nRow1, nRow2, nRow ); + ULONG nFormat = pPattern->GetNumberFormat( pFormatter ); + short nOldType = pFormatter->GetType( nFormat ); + if ( nOldType == nNewType || pFormatter->IsCompatible( nOldType, nNewType ) ) + nRow = nRow2; + else + { + SCROW nNewRow1 = Max( nRow1, nRow ); + SCROW nNewRow2 = Min( nRow2, nEndRow ); + pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, &aCache ); + nRow = nNewRow2; + } + } +} + + +void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet& rStyle ) +{ + const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow); + ScPatternAttr* pNewPattern = new ScPatternAttr(*pPattern); + if (pNewPattern) + { + pNewPattern->SetStyleSheet((ScStyleSheet*)&rStyle); + pAttrArray->SetPattern(nRow, pNewPattern, TRUE); + delete pNewPattern; + } +} + + +void ScColumn::ApplyStyleArea( SCROW nStartRow, SCROW nEndRow, const ScStyleSheet& rStyle ) +{ + pAttrArray->ApplyStyleArea(nStartRow, nEndRow, (ScStyleSheet*)&rStyle); +} + + +void ScColumn::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark) +{ + SCROW nTop; + SCROW nBottom; + + if ( rMark.IsMultiMarked() ) + { + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + while (aMarkIter.Next( nTop, nBottom )) + pAttrArray->ApplyStyleArea(nTop, nBottom, (ScStyleSheet*)&rStyle); + } +} + + +void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark, + const SvxBorderLine* pLine, BOOL bColorOnly ) +{ + if ( bColorOnly && !pLine ) + return; + + SCROW nTop; + SCROW nBottom; + + if (rMark.IsMultiMarked()) + { + ScMarkArrayIter aMarkIter( rMark.GetArray()+nCol ); + while (aMarkIter.Next( nTop, nBottom )) + pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly ); + } +} + + +const ScStyleSheet* ScColumn::GetStyle( SCROW nRow ) const +{ + return pAttrArray->GetPattern( nRow )->GetStyleSheet(); +} + + +const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, BOOL& rFound ) const +{ + rFound = FALSE; + if (!rMark.IsMultiMarked()) + { + DBG_ERROR("ScColumn::GetSelectionStyle ohne Selektion"); + return NULL; + } + + BOOL bEqual = TRUE; + + const ScStyleSheet* pStyle = NULL; + const ScStyleSheet* pNewStyle; + + ScMarkArrayIter aMarkIter( rMark.GetArray() + nCol ); + SCROW nTop; + SCROW nBottom; + while (bEqual && aMarkIter.Next( nTop, nBottom )) + { + ScAttrIterator aAttrIter( pAttrArray, nTop, nBottom ); + SCROW nRow; + SCROW nDummy; + const ScPatternAttr* pPattern; + while (bEqual && ( pPattern = aAttrIter.Next( nRow, nDummy ) ) != NULL) + { + pNewStyle = pPattern->GetStyleSheet(); + rFound = TRUE; + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + } + + return bEqual ? pStyle : NULL; +} + + +const ScStyleSheet* ScColumn::GetAreaStyle( BOOL& rFound, SCROW nRow1, SCROW nRow2 ) const +{ + rFound = FALSE; + + BOOL bEqual = TRUE; + + const ScStyleSheet* pStyle = NULL; + const ScStyleSheet* pNewStyle; + + ScAttrIterator aAttrIter( pAttrArray, nRow1, nRow2 ); + SCROW nRow; + SCROW nDummy; + const ScPatternAttr* pPattern; + while (bEqual && ( pPattern = aAttrIter.Next( nRow, nDummy ) ) != NULL) + { + pNewStyle = pPattern->GetStyleSheet(); + rFound = TRUE; + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + + return bEqual ? pStyle : NULL; +} + + +void ScColumn::FindStyleSheet( const SfxStyleSheetBase* pStyleSheet, BOOL* pUsed, BOOL bReset ) +{ + pAttrArray->FindStyleSheet( pStyleSheet, pUsed, bReset ); +} + + +BOOL ScColumn::IsStyleSheetUsed( const ScStyleSheet& rStyle, BOOL bGatherAllStyles ) const +{ + return pAttrArray->IsStyleSheetUsed( rStyle, bGatherAllStyles ); +} + + +BOOL ScColumn::ApplyFlags( SCROW nStartRow, SCROW nEndRow, INT16 nFlags ) +{ + return pAttrArray->ApplyFlags( nStartRow, nEndRow, nFlags ); +} + + +BOOL ScColumn::RemoveFlags( SCROW nStartRow, SCROW nEndRow, INT16 nFlags ) +{ + return pAttrArray->RemoveFlags( nStartRow, nEndRow, nFlags ); +} + + +void ScColumn::ClearItems( SCROW nStartRow, SCROW nEndRow, const USHORT* pWhich ) +{ + pAttrArray->ClearItems( nStartRow, nEndRow, pWhich ); +} + + +void ScColumn::SetPattern( SCROW nRow, const ScPatternAttr& rPatAttr, BOOL bPutToPool ) +{ + pAttrArray->SetPattern( nRow, &rPatAttr, bPutToPool ); +} + + +void ScColumn::SetPatternArea( SCROW nStartRow, SCROW nEndRow, + const ScPatternAttr& rPatAttr, BOOL bPutToPool ) +{ + pAttrArray->SetPatternArea( nStartRow, nEndRow, &rPatAttr, bPutToPool ); +} + + +void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr ) +{ + // um nur ein neues SetItem zu erzeugen, brauchen wir keinen SfxItemPoolCache. + //! Achtung: der SfxItemPoolCache scheint zuviele Refs fuer das neue SetItem zu erzeugen ?? + + ScDocumentPool* pDocPool = pDocument->GetPool(); + + const ScPatternAttr* pOldPattern = pAttrArray->GetPattern( nRow ); + ScPatternAttr* pTemp = new ScPatternAttr(*pOldPattern); + pTemp->GetItemSet().Put(rAttr); + const ScPatternAttr* pNewPattern = (const ScPatternAttr*) &pDocPool->Put( *pTemp ); + + if ( pNewPattern != pOldPattern ) + pAttrArray->SetPattern( nRow, pNewPattern ); + else + pDocPool->Remove( *pNewPattern ); // ausser Spesen nichts gewesen + + delete pTemp; + + // alte Version mit SfxItemPoolCache: +#if 0 + SfxItemPoolCache aCache( pDocument->GetPool(), &rAttr ); + + const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow ); + + // TRUE = alten Eintrag behalten + + ScPatternAttr* pNewPattern = (ScPatternAttr*) &aCache.ApplyTo( *pPattern, TRUE ); + ScDocumentPool::CheckRef( *pPattern ); + ScDocumentPool::CheckRef( *pNewPattern ); + + if (pNewPattern != pPattern) + pAttrArray->SetPattern( nRow, pNewPattern ); +#endif +} + +#ifdef _MSC_VER +#pragma optimize ( "", off ) +#endif + + +BOOL ScColumn::Search( SCROW nRow, SCSIZE& nIndex ) const +{ + if ( !pItems || !nCount ) + { + nIndex = 0; + return FALSE; + } + SCROW nMinRow = pItems[0].nRow; + if ( nRow <= nMinRow ) + { + nIndex = 0; + return nRow == nMinRow; + } + SCROW nMaxRow = pItems[nCount-1].nRow; + if ( nRow >= nMaxRow ) + { + if ( nRow == nMaxRow ) + { + nIndex = nCount - 1; + return TRUE; + } + else + { + nIndex = nCount; + return FALSE; + } + } + + long nOldLo, nOldHi; + long nLo = nOldLo = 0; + long nHi = nOldHi = Min(static_cast<long>(nCount)-1, static_cast<long>(nRow) ); + long i = 0; + BOOL bFound = FALSE; + // quite continuous distribution? => interpolating search + BOOL bInterpol = (static_cast<SCSIZE>(nMaxRow - nMinRow) < nCount * 2); + SCROW nR; + + while ( !bFound && nLo <= nHi ) + { + if ( !bInterpol || nHi - nLo < 3 ) + i = (nLo+nHi) / 2; // no effort, no division by zero + else + { // interpolating search + long nLoRow = pItems[nLo].nRow; // no unsigned underflow upon substraction + i = nLo + (long)((long)(nRow - nLoRow) * (nHi - nLo) + / (pItems[nHi].nRow - nLoRow)); + if ( i < 0 || static_cast<SCSIZE>(i) >= nCount ) + { // oops ... + i = (nLo+nHi) / 2; + bInterpol = FALSE; + } + } + nR = pItems[i].nRow; + if ( nR < nRow ) + { + nLo = i+1; + if ( bInterpol ) + { + if ( nLo <= nOldLo ) + bInterpol = FALSE; + else + nOldLo = nLo; + } + } + else + { + if ( nR > nRow ) + { + nHi = i-1; + if ( bInterpol ) + { + if ( nHi >= nOldHi ) + bInterpol = FALSE; + else + nOldHi = nHi; + } + } + else + bFound = TRUE; + } + } + if (bFound) + nIndex = static_cast<SCSIZE>(i); + else + nIndex = static_cast<SCSIZE>(nLo); // rear index + return bFound; +} + +#ifdef _MSC_VER +#pragma optimize ( "", on ) +#endif + + +ScBaseCell* ScColumn::GetCell( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + return pItems[nIndex].pCell; + return NULL; +} + + +void ScColumn::Resize( SCSIZE nSize ) +{ + if (nSize > sal::static_int_cast<SCSIZE>(MAXROWCOUNT)) + nSize = MAXROWCOUNT; + if (nSize < nCount) + nSize = nCount; + + ColEntry* pNewItems; + if (nSize) + { + SCSIZE nNewSize = nSize + COLUMN_DELTA - 1; + nNewSize -= nNewSize % COLUMN_DELTA; + nLimit = nNewSize; + pNewItems = new ColEntry[nLimit]; + } + else + { + nLimit = 0; + pNewItems = NULL; + } + if (pItems) + { + if (pNewItems) + memmove( pNewItems, pItems, nCount * sizeof(ColEntry) ); + delete[] pItems; + } + pItems = pNewItems; +} + +// SwapRow zum Sortieren + +namespace { + +/** Moves broadcaster from old cell to new cell if exists, otherwise creates a new note cell. */ +void lclTakeBroadcaster( ScBaseCell*& rpCell, SvtBroadcaster* pBC ) +{ + if( pBC ) + { + if( rpCell ) + rpCell->TakeBroadcaster( pBC ); + else + rpCell = new ScNoteCell( pBC ); + } +} + +} // namespace + +void ScColumn::SwapRow(SCROW nRow1, SCROW nRow2) +{ + /* Simple swap of cell pointers does not work if broadcasters exist (crash + if cell broadcasts directly or indirectly to itself). While swapping + the cells, broadcasters have to remain at old positions! */ + + /* While cloning cells, do not clone notes, but move note pointers to new + cells. This prevents creation of new caption drawing objects for every + swap operation while sorting. */ + + ScBaseCell* pCell1 = 0; + SCSIZE nIndex1; + if ( Search( nRow1, nIndex1 ) ) + pCell1 = pItems[nIndex1].pCell; + + ScBaseCell* pCell2 = 0; + SCSIZE nIndex2; + if ( Search( nRow2, nIndex2 ) ) + pCell2 = pItems[nIndex2].pCell; + + // no cells found, nothing to do + if ( !pCell1 && !pCell2 ) + return ; + + // swap variables if first cell is empty, to save some code below + if ( !pCell1 ) + { + ::std::swap( nRow1, nRow2 ); + ::std::swap( nIndex1, nIndex2 ); + ::std::swap( pCell1, pCell2 ); + } + + // from here: first cell (pCell1, nIndex1) exists always + + ScAddress aPos1( nCol, nRow1, nTab ); + ScAddress aPos2( nCol, nRow2, nTab ); + + CellType eType1 = pCell1->GetCellType(); + CellType eType2 = pCell2 ? pCell2->GetCellType() : CELLTYPE_NONE; + + ScFormulaCell* pFmlaCell1 = (eType1 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0; + ScFormulaCell* pFmlaCell2 = (eType2 == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell2 ) : 0; + + // simple swap if no formula cells present + if ( !pFmlaCell1 && !pFmlaCell2 ) + { + // remember cell broadcasters, must remain at old position + SvtBroadcaster* pBC1 = pCell1->ReleaseBroadcaster(); + + if ( pCell2 ) + { + /* Both cells exist, no formula cells involved, a simple swap can + be performed (but keep broadcasters and notes at old position). */ + pItems[nIndex1].pCell = pCell2; + pItems[nIndex2].pCell = pCell1; + + SvtBroadcaster* pBC2 = pCell2->ReleaseBroadcaster(); + pCell1->TakeBroadcaster( pBC2 ); + pCell2->TakeBroadcaster( pBC1 ); + + ScHint aHint1( SC_HINT_DATACHANGED, aPos1, pCell2 ); + pDocument->Broadcast( aHint1 ); + ScHint aHint2( SC_HINT_DATACHANGED, aPos2, pCell1 ); + pDocument->Broadcast( aHint2 ); + } + else + { + ScNoteCell* pDummyCell = pBC1 ? new ScNoteCell( pBC1 ) : 0; + if ( pDummyCell ) + { + // insert dummy note cell (without note) containing old broadcaster + pItems[nIndex1].pCell = pDummyCell; + } + else + { + // remove ColEntry at old position + --nCount; + memmove( &pItems[nIndex1], &pItems[nIndex1 + 1], (nCount - nIndex1) * sizeof(ColEntry) ); + pItems[nCount].nRow = 0; + pItems[nCount].pCell = 0; + } + + // insert ColEntry at new position + Insert( nRow2, pCell1 ); + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, aPos1, pDummyCell ) ); + } + + return; + } + + // from here: at least one of the cells is a formula cell + + /* Never move any array formulas. Disabling sort if parts of array + formulas are contained is done at UI. */ + if ( (pFmlaCell1 && (pFmlaCell1->GetMatrixFlag() != 0)) || (pFmlaCell2 && (pFmlaCell2->GetMatrixFlag() != 0)) ) + return; + + // do not swap, if formulas are equal + if ( pFmlaCell1 && pFmlaCell2 ) + { + ScTokenArray* pCode1 = pFmlaCell1->GetCode(); + ScTokenArray* pCode2 = pFmlaCell2->GetCode(); + + if (pCode1->GetLen() == pCode2->GetLen()) // nicht-UPN + { + BOOL bEqual = TRUE; + USHORT nLen = pCode1->GetLen(); + FormulaToken** ppToken1 = pCode1->GetArray(); + FormulaToken** ppToken2 = pCode2->GetArray(); + for (USHORT i=0; i<nLen; i++) + { + if ( !ppToken1[i]->TextEqual(*(ppToken2[i])) || + ppToken1[i]->Is3DRef() || ppToken2[i]->Is3DRef() ) + { + bEqual = FALSE; + break; + } + } + + // do not swap formula cells with equal formulas, but swap notes + if (bEqual) + { + ScPostIt* pNote1 = pCell1->ReleaseNote(); + pCell1->TakeNote( pCell2->ReleaseNote() ); + pCell2->TakeNote( pNote1 ); + return; + } + } + } + + // hier kein UpdateReference wegen #30529# - mitsortiert werden nur noch relative Referenzen +// long dy = (long)nRow2 - (long)nRow1; + + /* Create clone of pCell1 at position of pCell2 (pCell1 exists always, see + variable swapping above). Do not clone the note, but move pointer of + old note to new cell. */ + ScBaseCell* pNew2 = pCell1->CloneWithoutNote( *pDocument, aPos2, SC_CLONECELL_ADJUST3DREL ); + pNew2->TakeNote( pCell1->ReleaseNote() ); + + /* Create clone of pCell2 at position of pCell1. Do not clone the note, + but move pointer of old note to new cell. */ + ScBaseCell* pNew1 = 0; + if ( pCell2 ) + { + pNew1 = pCell2->CloneWithoutNote( *pDocument, aPos1, SC_CLONECELL_ADJUST3DREL ); + pNew1->TakeNote( pCell2->ReleaseNote() ); + } + + // move old broadcasters new cells at the same old position + SvtBroadcaster* pBC1 = pCell1->ReleaseBroadcaster(); + lclTakeBroadcaster( pNew1, pBC1 ); + SvtBroadcaster* pBC2 = pCell2 ? pCell2->ReleaseBroadcaster() : 0; + lclTakeBroadcaster( pNew2, pBC2 ); + + /* Insert the new cells. Old cell has to be deleted, if there is no new + cell (call to Insert deletes old cell by itself). */ + if ( !pNew1 ) + Delete( nRow1 ); // deletes pCell1 + else + Insert( nRow1, pNew1 ); // deletes pCell1, inserts pNew1 + + if ( pCell2 && !pNew2 ) + Delete( nRow2 ); // deletes pCell2 + else if ( pNew2 ) + Insert( nRow2, pNew2 ); // deletes pCell2 (if existing), inserts pNew2 + + // #64122# Bei Formeln hinterher nochmal broadcasten, damit die Formel nicht in irgendwelchen + // FormulaTrack-Listen landet, ohne die Broadcaster beruecksichtigt zu haben + // (erst hier, wenn beide Zellen eingefuegt sind) + if ( pBC1 && pFmlaCell2 ) + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, aPos1, pNew1 ) ); + if ( pBC2 && pFmlaCell1 ) + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, aPos2, pNew2 ) ); +} + + +void ScColumn::SwapCell( SCROW nRow, ScColumn& rCol) +{ + ScBaseCell* pCell1 = 0; + SCSIZE nIndex1; + if ( Search( nRow, nIndex1 ) ) + pCell1 = pItems[nIndex1].pCell; + + ScBaseCell* pCell2 = 0; + SCSIZE nIndex2; + if ( rCol.Search( nRow, nIndex2 ) ) + pCell2 = rCol.pItems[nIndex2].pCell; + + // reverse call if own cell is missing (ensures own existing cell in following code) + if( !pCell1 ) + { + if( pCell2 ) + rCol.SwapCell( nRow, *this ); + return; + } + + // from here: own cell (pCell1, nIndex1) exists always + + ScFormulaCell* pFmlaCell1 = (pCell1->GetCellType() == CELLTYPE_FORMULA) ? static_cast< ScFormulaCell* >( pCell1 ) : 0; + ScFormulaCell* pFmlaCell2 = (pCell2 && (pCell2->GetCellType() == CELLTYPE_FORMULA)) ? static_cast< ScFormulaCell* >( pCell2 ) : 0; + + if ( pCell2 ) + { + // Tauschen + pItems[nIndex1].pCell = pCell2; + rCol.pItems[nIndex2].pCell = pCell1; + // Referenzen aktualisieren + SCsCOL dx = rCol.nCol - nCol; + if ( pFmlaCell1 ) + { + ScRange aRange( ScAddress( rCol.nCol, 0, nTab ), + ScAddress( rCol.nCol, MAXROW, nTab ) ); + pFmlaCell1->aPos.SetCol( rCol.nCol ); + pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0); + } + if ( pFmlaCell2 ) + { + ScRange aRange( ScAddress( nCol, 0, nTab ), + ScAddress( nCol, MAXROW, nTab ) ); + pFmlaCell2->aPos.SetCol( nCol ); + pFmlaCell2->UpdateReference(URM_MOVE, aRange, -dx, 0, 0); + } + } + else + { + // Loeschen + --nCount; + memmove( &pItems[nIndex1], &pItems[nIndex1 + 1], (nCount - nIndex1) * sizeof(ColEntry) ); + pItems[nCount].nRow = 0; + pItems[nCount].pCell = 0; + // Referenzen aktualisieren + SCsCOL dx = rCol.nCol - nCol; + if ( pFmlaCell1 ) + { + ScRange aRange( ScAddress( rCol.nCol, 0, nTab ), + ScAddress( rCol.nCol, MAXROW, nTab ) ); + pFmlaCell1->aPos.SetCol( rCol.nCol ); + pFmlaCell1->UpdateReference(URM_MOVE, aRange, dx, 0, 0); + } + // Einfuegen + rCol.Insert(nRow, pCell1); + } +} + + +BOOL ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const +{ + if (!IsEmpty()) + { + BOOL bTest = TRUE; + if (pItems) + for (SCSIZE i=0; (i<nCount) && bTest; i++) + bTest = (pItems[i].nRow < nStartRow) || (pItems[i].nRow > nEndRow) + || pItems[i].pCell->IsBlank(); + + // AttrArray testet nur zusammengefasste + + if ((bTest) && (pAttrArray)) + bTest = pAttrArray->TestInsertCol(nStartRow, nEndRow); + + //! rausgeschobene Attribute bei Undo beruecksichtigen + + return bTest; + } + else + return TRUE; +} + + +BOOL ScColumn::TestInsertRow( SCSIZE nSize ) const +{ + // AttrArray only looks for merged cells + + if ( pItems && nCount ) + return ( nSize <= sal::static_int_cast<SCSIZE>(MAXROW) && + pItems[nCount-1].nRow <= MAXROW-(SCROW)nSize && pAttrArray->TestInsertRow( nSize ) ); + else + return pAttrArray->TestInsertRow( nSize ); + +#if 0 + //! rausgeschobene Attribute bei Undo beruecksichtigen + + if ( nSize > static_cast<SCSIZE>(MAXROW) ) + return FALSE; + + SCSIZE nVis = nCount; + while ( nVis && pItems[nVis-1].pCell->IsBlank() ) + --nVis; + + if ( nVis ) + return ( pItems[nVis-1].nRow <= MAXROW-nSize ); + else + return TRUE; +#endif +} + + +void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize ) +{ + pAttrArray->InsertRow( nStartRow, nSize ); + + //! Search + + if ( !pItems || !nCount ) + return; + + SCSIZE i; + Search( nStartRow, i ); + if ( i >= nCount ) + return ; + + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + SCSIZE nNewCount = nCount; + BOOL bCountChanged = FALSE; + ScAddress aAdr( nCol, 0, nTab ); + ScHint aHint( SC_HINT_DATACHANGED, aAdr, NULL ); // only areas (ScBaseCell* == NULL) + ScAddress& rAddress = aHint.GetAddress(); + // for sparse occupation use single broadcasts, not ranges + BOOL bSingleBroadcasts = (((pItems[nCount-1].nRow - pItems[i].nRow) / + (nCount - i)) > 1); + if ( bSingleBroadcasts ) + { + SCROW nLastBroadcast = MAXROW+1; + for ( ; i < nCount; i++) + { + SCROW nOldRow = pItems[i].nRow; + // #43940# Aenderung Quelle broadcasten + if ( nLastBroadcast != nOldRow ) + { // direkt aufeinanderfolgende nicht doppelt broadcasten + rAddress.SetRow( nOldRow ); + pDocument->AreaBroadcast( aHint ); + } + SCROW nNewRow = (pItems[i].nRow += nSize); + // #43940# Aenderung Ziel broadcasten + rAddress.SetRow( nNewRow ); + pDocument->AreaBroadcast( aHint ); + nLastBroadcast = nNewRow; + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + if ( nNewRow > MAXROW && !bCountChanged ) + { + nNewCount = i; + bCountChanged = TRUE; + } + } + } + else + { + rAddress.SetRow( pItems[i].nRow ); + ScRange aRange( rAddress ); + for ( ; i < nCount; i++) + { + SCROW nNewRow = (pItems[i].nRow += nSize); + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + if ( nNewRow > MAXROW && !bCountChanged ) + { + nNewCount = i; + bCountChanged = TRUE; + aRange.aEnd.SetRow( MAXROW ); + } + } + if ( !bCountChanged ) + aRange.aEnd.SetRow( pItems[nCount-1].nRow ); + pDocument->AreaBroadcastInRange( aRange, aHint ); + } + + if (bCountChanged) + { + SCSIZE nDelCount = nCount - nNewCount; + ScBaseCell** ppDelCells = new ScBaseCell*[nDelCount]; + SCROW* pDelRows = new SCROW[nDelCount]; + for (i = 0; i < nDelCount; i++) + { + ppDelCells[i] = pItems[nNewCount+i].pCell; + pDelRows[i] = pItems[nNewCount+i].nRow; + } + nCount = nNewCount; + + for (i = 0; i < nDelCount; i++) + { + ScBaseCell* pCell = ppDelCells[i]; + DBG_ASSERT( pCell->IsBlank(), "sichtbare Zelle weggeschoben" ); + SvtBroadcaster* pBC = pCell->GetBroadcaster(); + if (pBC) + { + MoveListeners( *pBC, pDelRows[i] - nSize ); + pCell->DeleteBroadcaster(); + pCell->Delete(); + } + } + + delete [] pDelRows; + delete [] ppDelCells; + } + + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::CopyToClip(SCROW nRow1, SCROW nRow2, ScColumn& rColumn, BOOL bKeepScenarioFlags, BOOL bCloneNoteCaptions) +{ + pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray, + bKeepScenarioFlags ? (SC_MF_ALL & ~SC_MF_SCENARIO) : SC_MF_ALL ); + + SCSIZE i; + SCSIZE nBlockCount = 0; + SCSIZE nStartIndex = 0, nEndIndex = 0; + for (i = 0; i < nCount; i++) + if ((pItems[i].nRow >= nRow1) && (pItems[i].nRow <= nRow2)) + { + if (!nBlockCount) + nStartIndex = i; + nEndIndex = i; + ++nBlockCount; + + // im Clipboard muessen interpretierte Zellen stehen, um andere Formate + // (Text, Grafik...) erzueugen zu koennen + + if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = (ScFormulaCell*) pItems[i].pCell; + if (pFCell->GetDirty() && pDocument->GetAutoCalc()) + pFCell->Interpret(); + } + } + + if (nBlockCount) + { + int nCloneFlags = bCloneNoteCaptions ? SC_CLONECELL_DEFAULT : SC_CLONECELL_NOCAPTION; + rColumn.Resize( rColumn.GetCellCount() + nBlockCount ); + ScAddress aOwnPos( nCol, 0, nTab ); + ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab ); + for (i = nStartIndex; i <= nEndIndex; i++) + { + aOwnPos.SetRow( pItems[i].nRow ); + aDestPos.SetRow( pItems[i].nRow ); + ScBaseCell* pNewCell = pItems[i].pCell->CloneWithNote( aOwnPos, *rColumn.pDocument, aDestPos, nCloneFlags ); + rColumn.Append( aDestPos.Row(), pNewCell ); + } + } +} + + +void ScColumn::CopyToColumn(SCROW nRow1, SCROW nRow2, USHORT nFlags, BOOL bMarked, + ScColumn& rColumn, const ScMarkData* pMarkData, BOOL bAsLink ) +{ + if (bMarked) + { + SCROW nStart, nEnd; + if (pMarkData && pMarkData->IsMultiMarked()) + { + ScMarkArrayIter aIter( pMarkData->GetArray()+nCol ); + + while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 ) + { + if ( nEnd >= nRow1 ) + CopyToColumn( Max(nRow1,nStart), Min(nRow2,nEnd), + nFlags, FALSE, rColumn, pMarkData, bAsLink ); + } + } + else + { + DBG_ERROR("CopyToColumn: bMarked, aber keine Markierung"); + } + return; + } + + if ( (nFlags & IDF_ATTRIB) != 0 ) + { + if ( (nFlags & IDF_STYLES) != IDF_STYLES ) + { // StyleSheets im Zieldokument bleiben erhalten + // z.B. DIF und RTF Clipboard-Import + for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ ) + { + const ScStyleSheet* pStyle = + rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet(); + const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow ); + ScPatternAttr* pNewPattern = new ScPatternAttr( *pPattern ); + pNewPattern->SetStyleSheet( (ScStyleSheet*)pStyle ); + rColumn.pAttrArray->SetPattern( nRow, pNewPattern, TRUE ); + delete pNewPattern; + } + } + else + pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray); + } + + + if ((nFlags & IDF_CONTENTS) != 0) + { + SCSIZE i; + SCSIZE nBlockCount = 0; + SCSIZE nStartIndex = 0, nEndIndex = 0; + for (i = 0; i < nCount; i++) + if ((pItems[i].nRow >= nRow1) && (pItems[i].nRow <= nRow2)) + { + if (!nBlockCount) + nStartIndex = i; + nEndIndex = i; + ++nBlockCount; + } + + if (nBlockCount) + { + rColumn.Resize( rColumn.GetCellCount() + nBlockCount ); + ScAddress aDestPos( rColumn.nCol, 0, rColumn.nTab ); + for (i = nStartIndex; i <= nEndIndex; i++) + { + aDestPos.SetRow( pItems[i].nRow ); + ScBaseCell* pNew = bAsLink ? + CreateRefCell( rColumn.pDocument, aDestPos, i, nFlags ) : + CloneCell( i, nFlags, *rColumn.pDocument, aDestPos ); + + if (pNew) + rColumn.Insert(pItems[i].nRow, pNew); + } + } + } +} + + +void ScColumn::UndoToColumn(SCROW nRow1, SCROW nRow2, USHORT nFlags, BOOL bMarked, + ScColumn& rColumn, const ScMarkData* pMarkData ) +{ + if (nRow1 > 0) + CopyToColumn( 0, nRow1-1, IDF_FORMULA, FALSE, rColumn ); + + CopyToColumn( nRow1, nRow2, nFlags, bMarked, rColumn, pMarkData ); //! bMarked ???? + + if (nRow2 < MAXROW) + CopyToColumn( nRow2+1, MAXROW, IDF_FORMULA, FALSE, rColumn ); +} + + +void ScColumn::CopyUpdated( const ScColumn& rPosCol, ScColumn& rDestCol ) const +{ + ScDocument& rDestDoc = *rDestCol.pDocument; + ScAddress aOwnPos( nCol, 0, nTab ); + ScAddress aDestPos( rDestCol.nCol, 0, rDestCol.nTab ); + + SCSIZE nPosCount = rPosCol.nCount; + for (SCSIZE nPosIndex = 0; nPosIndex < nPosCount; nPosIndex++) + { + aOwnPos.SetRow( rPosCol.pItems[nPosIndex].nRow ); + aDestPos.SetRow( aOwnPos.Row() ); + SCSIZE nThisIndex; + if ( Search( aDestPos.Row(), nThisIndex ) ) + { + ScBaseCell* pNew = pItems[nThisIndex].pCell->CloneWithNote( aOwnPos, rDestDoc, aDestPos ); + rDestCol.Insert( aDestPos.Row(), pNew ); + } + } + + // Dummy: + // CopyToColumn( 0,MAXROW, IDF_FORMULA, FALSE, rDestCol, NULL, FALSE ); +} + + +void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol ) +{ + // Dies ist die Szenario-Tabelle, die Daten werden hineinkopiert + + ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW ); + SCROW nStart, nEnd; + const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd ); + while (pPattern) + { + if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() ) + { + DeleteArea( nStart, nEnd, IDF_CONTENTS ); + ((ScColumn&)rSrcCol). + CopyToColumn( nStart, nEnd, IDF_CONTENTS, FALSE, *this ); + + // UpdateUsed nicht noetig, schon in TestCopyScenario passiert + + SCsTAB nDz = nTab - rSrcCol.nTab; + UpdateReference(URM_COPY, nCol, nStart, nTab, + nCol, nEnd, nTab, + 0, 0, nDz, NULL); + UpdateCompile(); + } + + //! CopyToColumn "const" machen !!! + + pPattern = aAttrIter.Next( nStart, nEnd ); + } +} + + +void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const +{ + // Dies ist die Szenario-Tabelle, die Daten werden in die andere kopiert + + ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW ); + SCROW nStart, nEnd; + const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd ); + while (pPattern) + { + if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() ) + { + rDestCol.DeleteArea( nStart, nEnd, IDF_CONTENTS ); + ((ScColumn*)this)-> + CopyToColumn( nStart, nEnd, IDF_CONTENTS, FALSE, rDestCol ); + + // UpdateUsed nicht noetig, schon in TestCopyScenario passiert + + SCsTAB nDz = rDestCol.nTab - nTab; + rDestCol.UpdateReference(URM_COPY, rDestCol.nCol, nStart, rDestCol.nTab, + rDestCol.nCol, nEnd, rDestCol.nTab, + 0, 0, nDz, NULL); + rDestCol.UpdateCompile(); + } + + //! CopyToColumn "const" machen !!! + + pPattern = aAttrIter.Next( nStart, nEnd ); + } +} + + +BOOL ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const +{ + BOOL bOk = TRUE; + ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW ); + SCROW nStart = 0, nEnd = 0; + const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd ); + while (pPattern && bOk) + { + if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() ) + if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HASATTR_PROTECTED ) ) + bOk = FALSE; + + pPattern = aAttrIter.Next( nStart, nEnd ); + } + return bOk; +} + + +void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const +{ + ScRange aRange( nCol, 0, nTab ); + + ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW ); + SCROW nStart, nEnd; + const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd ); + while (pPattern) + { + if ( ((ScMergeFlagAttr&)pPattern->GetItem( ATTR_MERGE_FLAG )).IsScenario() ) + { + aRange.aStart.SetRow( nStart ); + aRange.aEnd.SetRow( nEnd ); + rDestMark.SetMultiMarkArea( aRange, TRUE ); + } + + pPattern = aAttrIter.Next( nStart, nEnd ); + } +} + + +void ScColumn::SwapCol(ScColumn& rCol) +{ + SCSIZE nTemp; + + nTemp = rCol.nCount; + rCol.nCount = nCount; + nCount = nTemp; + + nTemp = rCol.nLimit; + rCol.nLimit = nLimit; + nLimit = nTemp; + + ColEntry* pTempItems = rCol.pItems; + rCol.pItems = pItems; + pItems = pTempItems; + + ScAttrArray* pTempAttr = rCol.pAttrArray; + rCol.pAttrArray = pAttrArray; + pAttrArray = pTempAttr; + + // #38415# AttrArray muss richtige Spaltennummer haben + pAttrArray->SetCol(nCol); + rCol.pAttrArray->SetCol(rCol.nCol); + + SCSIZE i; + if (pItems) + for (i = 0; i < nCount; i++) + { + ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + pCell->aPos.SetCol(nCol); + } + if (rCol.pItems) + for (i = 0; i < rCol.nCount; i++) + { + ScFormulaCell* pCell = (ScFormulaCell*) rCol.pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + pCell->aPos.SetCol(rCol.nCol); + } +} + + +void ScColumn::MoveTo(SCROW nStartRow, SCROW nEndRow, ScColumn& rCol) +{ + pAttrArray->MoveTo(nStartRow, nEndRow, *rCol.pAttrArray); + + if (pItems) + { + ::std::vector<SCROW> aRows; + bool bConsecutive = true; + SCSIZE i; + Search( nStartRow, i); // i points to start row or position thereafter + SCSIZE nStartPos = i; + for ( ; i < nCount && pItems[i].nRow <= nEndRow; ++i) + { + SCROW nRow = pItems[i].nRow; + aRows.push_back( nRow); + rCol.Insert( nRow, pItems[i].pCell); + if (nRow != pItems[i].nRow) + { // Listener inserted + bConsecutive = false; + Search( nRow, i); + } + } + SCSIZE nStopPos = i; + if (nStartPos < nStopPos) + { + // Create list of ranges of cell entry positions + typedef ::std::pair<SCSIZE,SCSIZE> PosPair; + typedef ::std::vector<PosPair> EntryPosPairs; + EntryPosPairs aEntries; + if (bConsecutive) + aEntries.push_back( PosPair(nStartPos, nStopPos)); + else + { + bool bFirst = true; + nStopPos = 0; + for (::std::vector<SCROW>::const_iterator it( aRows.begin()); + it != aRows.end() && nStopPos < nCount; ++it, + ++nStopPos) + { + if (!bFirst && *it != pItems[nStopPos].nRow) + { + aEntries.push_back( PosPair(nStartPos, nStopPos)); + bFirst = true; + } + if (bFirst && Search( *it, nStartPos)) + { + bFirst = false; + nStopPos = nStartPos; + } + } + if (!bFirst && nStartPos < nStopPos) + aEntries.push_back( PosPair(nStartPos, nStopPos)); + } + // Broadcast changes + ScAddress aAdr( nCol, 0, nTab ); + ScHint aHint( SC_HINT_DYING, aAdr, NULL ); // areas only + ScAddress& rAddress = aHint.GetAddress(); + ScNoteCell* pNoteCell = new ScNoteCell; // Dummy like in DeleteRange + + // #121990# must iterate backwards, because indexes of following cells become invalid + for (EntryPosPairs::reverse_iterator it( aEntries.rbegin()); + it != aEntries.rend(); ++it) + { + nStartPos = (*it).first; + nStopPos = (*it).second; + for (i=nStartPos; i<nStopPos; ++i) + pItems[i].pCell = pNoteCell; + for (i=nStartPos; i<nStopPos; ++i) + { + rAddress.SetRow( pItems[i].nRow ); + pDocument->AreaBroadcast( aHint ); + } + nCount -= nStopPos - nStartPos; + memmove( &pItems[nStartPos], &pItems[nStopPos], + (nCount - nStartPos) * sizeof(ColEntry) ); + } + delete pNoteCell; + pItems[nCount].nRow = 0; + pItems[nCount].pCell = NULL; + } + } +} + + +void ScColumn::UpdateReference( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScDocument* pUndoDoc ) +{ + if (pItems) + { + ScRange aRange( ScAddress( nCol1, nRow1, nTab1 ), + ScAddress( nCol2, nRow2, nTab2 ) ); + if ( eUpdateRefMode == URM_COPY && nRow1 == nRow2 ) + { // z.B. eine einzelne Zelle aus dem Clipboard eingefuegt + SCSIZE nIndex; + if ( Search( nRow1, nIndex ) ) + { + ScFormulaCell* pCell = (ScFormulaCell*) pItems[nIndex].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + pCell->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc ); + } + } + else + { + // #90279# For performance reasons two loop bodies instead of + // testing for update mode in each iteration. + // Anyways, this is still a bottleneck on large arrays with few + // formulas cells. + if ( eUpdateRefMode == URM_COPY ) + { + SCSIZE i; + Search( nRow1, i ); + for ( ; i < nCount; i++ ) + { + SCROW nRow = pItems[i].nRow; + if ( nRow > nRow2 ) + break; + ScBaseCell* pCell = pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + { + ((ScFormulaCell*)pCell)->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener removed/inserted? + } + } + } + else + { + SCSIZE i = 0; + for ( ; i < nCount; i++ ) + { + ScBaseCell* pCell = pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCROW nRow = pItems[i].nRow; + // When deleting rows on several sheets, the formula's position may be updated with the first call, + // so the undo position must be passed from here. + ScAddress aUndoPos( nCol, nRow, nTab ); + ((ScFormulaCell*)pCell)->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz, pUndoDoc, &aUndoPos ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener removed/inserted? + } + } + } + } + } +} + + +void ScColumn::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, + ScDocument* pUndoDoc ) +{ + if (pItems) + for (SCSIZE i=0; i<nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCROW nRow = pItems[i].nRow; + ((ScFormulaCell*)pCell)->UpdateTranspose( rSource, rDest, pUndoDoc ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + if (pItems) + for (SCSIZE i=0; i<nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCROW nRow = pItems[i].nRow; + ((ScFormulaCell*)pCell)->UpdateGrow( rArea, nGrowX, nGrowY ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::UpdateInsertTab( SCTAB nTable) +{ + if (nTab >= nTable) + pAttrArray->SetTab(++nTab); + if( pItems ) + UpdateInsertTabOnlyCells( nTable ); +} + + +void ScColumn::UpdateInsertTabOnlyCells( SCTAB nTable) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCROW nRow = pItems[i].nRow; + pCell->UpdateInsertTab(nTable); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::UpdateInsertTabAbs(SCTAB nTable) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell; + if( pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCROW nRow = pItems[i].nRow; + pCell->UpdateInsertTabAbs(nTable); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::UpdateDeleteTab( SCTAB nTable, BOOL bIsMove, ScColumn* pRefUndo ) +{ + if (nTab > nTable) + pAttrArray->SetTab(--nTab); + + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + ScFormulaCell* pOld = (ScFormulaCell*)pItems[i].pCell; + + /* Do not copy cell note to the undo document. Undo will copy + back the formula cell while keeping the original note. */ + ScBaseCell* pSave = pRefUndo ? pOld->CloneWithoutNote( *pDocument ) : 0; + + BOOL bChanged = pOld->UpdateDeleteTab(nTable, bIsMove); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + + if (pRefUndo) + { + if (bChanged) + pRefUndo->Insert( nRow, pSave ); + else if(pSave) + pSave->Delete(); + } + } +} + + +void ScColumn::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo ) +{ + nTab = nTabNo; + pAttrArray->SetTab( nTabNo ); + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScFormulaCell* pCell = (ScFormulaCell*) pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + pCell->UpdateMoveTab( nOldPos, nNewPos, nTabNo ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::UpdateCompile( BOOL bForceIfNameInUse ) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; + if( p->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + p->UpdateCompile( bForceIfNameInUse ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::SetTabNo(SCTAB nNewTab) +{ + nTab = nNewTab; + pAttrArray->SetTab( nNewTab ); + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; + if( p->GetCellType() == CELLTYPE_FORMULA ) + p->aPos.SetTab( nNewTab ); + } +} + + +BOOL ScColumn::IsRangeNameInUse(SCROW nRow1, SCROW nRow2, USHORT nIndex) const +{ + BOOL bInUse = FALSE; + if (pItems) + for (SCSIZE i = 0; !bInUse && (i < nCount); i++) + if ((pItems[i].nRow >= nRow1) && + (pItems[i].nRow <= nRow2) && + (pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA)) + bInUse = ((ScFormulaCell*)pItems[i].pCell)->IsRangeNameInUse(nIndex); + return bInUse; +} + +void ScColumn::FindRangeNamesInUse(SCROW nRow1, SCROW nRow2, std::set<USHORT>& rIndexes) const +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + if ((pItems[i].nRow >= nRow1) && + (pItems[i].nRow <= nRow2) && + (pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA)) + ((ScFormulaCell*)pItems[i].pCell)->FindRangeNamesInUse(rIndexes); +} + +void ScColumn::ReplaceRangeNamesInUse(SCROW nRow1, SCROW nRow2, + const ScRangeData::IndexMap& rMap ) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + if ((pItems[i].nRow >= nRow1) && + (pItems[i].nRow <= nRow2) && + (pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA)) + { + SCROW nRow = pItems[i].nRow; + ((ScFormulaCell*)pItems[i].pCell)->ReplaceRangeNamesInUse( rMap ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + +void ScColumn::SetDirtyVar() +{ + for (SCSIZE i=0; i<nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; + if( p->GetCellType() == CELLTYPE_FORMULA ) + p->SetDirtyVar(); + } +} + + +void ScColumn::SetDirty() +{ + // wird nur dokumentweit verwendet, kein FormulaTrack + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCSIZE i=0; i<nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; + if( p->GetCellType() == CELLTYPE_FORMULA ) + { + p->SetDirtyVar(); + if ( !pDocument->IsInFormulaTree( p ) ) + pDocument->PutInFormulaTree( p ); + } + } + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::SetDirty( const ScRange& rRange ) +{ // broadcastet alles innerhalb eines Range, mit FormulaTrack + if ( !pItems || !nCount ) + return ; + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + SCROW nRow2 = rRange.aEnd.Row(); + ScAddress aPos( nCol, 0, nTab ); + ScHint aHint( SC_HINT_DATACHANGED, aPos, NULL ); + SCROW nRow; + SCSIZE nIndex; + Search( rRange.aStart.Row(), nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->SetDirty(); + else + { + aHint.GetAddress().SetRow( nRow ); + aHint.SetCell( pCell ); + pDocument->Broadcast( aHint ); + } + nIndex++; + } + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::SetTableOpDirty( const ScRange& rRange ) +{ + if ( !pItems || !nCount ) + return ; + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // no multiple recalculation + SCROW nRow2 = rRange.aEnd.Row(); + ScAddress aPos( nCol, 0, nTab ); + ScHint aHint( SC_HINT_TABLEOPDIRTY, aPos, NULL ); + SCROW nRow; + SCSIZE nIndex; + Search( rRange.aStart.Row(), nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->SetTableOpDirty(); + else + { + aHint.GetAddress().SetRow( nRow ); + aHint.SetCell( pCell ); + pDocument->Broadcast( aHint ); + } + nIndex++; + } + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::SetDirtyAfterLoad() +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCSIZE i=0; i<nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; +#if 1 + // Simply set dirty and append to FormulaTree, without broadcasting, + // which is a magnitude faster. This is used to calculate the entire + // document, e.g. when loading alien file formats. + if ( p->GetCellType() == CELLTYPE_FORMULA ) + p->SetDirtyAfterLoad(); +#else +/* This was used with the binary file format that stored results, where only + * newly compiled and volatile functions and their dependents had to be + * recalculated, which was faster then. Since that was moved to 'binfilter' to + * convert to an XML file this isn't needed anymore, and not used for other + * file formats. Kept for reference in case mechanism needs to be reactivated + * for some file formats, we'd have to introduce a controlling parameter to + * this method here then. +*/ + + // If the cell was alsready dirty because of CalcAfterLoad, + // FormulaTracking has to take place. + if ( p->GetCellType() == CELLTYPE_FORMULA && p->GetDirty() ) + p->SetDirty(); +#endif + } + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::SetRelNameDirty() +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCSIZE i=0; i<nCount; i++) + { + ScFormulaCell* p = (ScFormulaCell*) pItems[i].pCell; + if( p->GetCellType() == CELLTYPE_FORMULA && p->HasRelNameReference() ) + p->SetDirty(); + } + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::CalcAll() +{ + if (pItems) + for (SCSIZE i=0; i<nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { +#if OSL_DEBUG_LEVEL > 1 + // nach F9 ctrl-F9: ueberprueft die Berechnung per FormulaTree + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + double nOldVal, nNewVal; + nOldVal = pFCell->GetValue(); +#endif + ((ScFormulaCell*)pCell)->Interpret(); +#if OSL_DEBUG_LEVEL > 1 + if ( pFCell->GetCode()->IsRecalcModeNormal() ) + nNewVal = pFCell->GetValue(); + else + nNewVal = nOldVal; // random(), jetzt() etc. + DBG_ASSERT( nOldVal==nNewVal, "CalcAll: nOldVal != nNewVal" ); +#endif + } + } +} + + +void ScColumn::CompileAll() +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + // fuer unbedingtes kompilieren + // bCompile=TRUE und pCode->nError=0 + ((ScFormulaCell*)pCell)->GetCode()->SetCodeError( 0 ); + ((ScFormulaCell*)pCell)->SetCompile( TRUE ); + ((ScFormulaCell*)pCell)->CompileTokenArray(); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::CompileXML( ScProgress& rProgress ) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + ((ScFormulaCell*)pCell)->CompileXML( rProgress ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener geloescht/eingefuegt? + } + } +} + + +void ScColumn::CalcAfterLoad() +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->CalcAfterLoad(); + } +} + + +bool ScColumn::MarkUsedExternalReferences() +{ + bool bAllMarked = false; + if (pItems) + { + for (SCSIZE i = 0; i < nCount && !bAllMarked; ++i) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + bAllMarked = ((ScFormulaCell*)pCell)->MarkUsedExternalReferences(); + } + } + return bAllMarked; +} + + +void ScColumn::ResetChanged( SCROW nStartRow, SCROW nEndRow ) +{ + if (pItems) + { + SCSIZE nIndex; + Search(nStartRow,nIndex); + while (nIndex<nCount && pItems[nIndex].nRow <= nEndRow) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + ((ScFormulaCell*)pCell)->ResetChanged(); + ++nIndex; + } + } +} + + +BOOL ScColumn::HasEditCells(SCROW nStartRow, SCROW nEndRow, SCROW& rFirst) const +{ + // used in GetOptimalHeight - ambiguous script type counts as edit cell + + SCROW nRow = 0; + SCSIZE nIndex; + Search(nStartRow,nIndex); + while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : FALSE ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + CellType eCellType = pCell->GetCellType(); + if ( eCellType == CELLTYPE_EDIT || + IsAmbiguousScriptNonZero( pDocument->GetScriptType(nCol, nRow, nTab, pCell) ) || + ((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) ) + { + rFirst = nRow; + return TRUE; + } + ++nIndex; + } + + return FALSE; +} + + +SCsROW ScColumn::SearchStyle( SCsROW nRow, const ScStyleSheet* pSearchStyle, + BOOL bUp, BOOL bInSelection, const ScMarkData& rMark ) +{ + if (bInSelection) + { + if (rMark.IsMultiMarked()) + return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp, + (ScMarkArray*) rMark.GetArray()+nCol ); //! const + else + return -1; + } + else + return pAttrArray->SearchStyle( nRow, pSearchStyle, bUp, NULL ); +} + + +BOOL ScColumn::SearchStyleRange( SCsROW& rRow, SCsROW& rEndRow, const ScStyleSheet* pSearchStyle, + BOOL bUp, BOOL bInSelection, const ScMarkData& rMark ) +{ + if (bInSelection) + { + if (rMark.IsMultiMarked()) + return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp, + (ScMarkArray*) rMark.GetArray()+nCol ); //! const + else + return FALSE; + } + else + return pAttrArray->SearchStyleRange( rRow, rEndRow, pSearchStyle, bUp, NULL ); +} + + diff --git a/sc/source/core/data/column2.cxx b/sc/source/core/data/column2.cxx new file mode 100644 index 000000000000..01c72ab4e0c4 --- /dev/null +++ b/sc/source/core/data/column2.cxx @@ -0,0 +1,1862 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: column2.cxx,v $ + * $Revision: 1.32.126.6 $ + * + * 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 "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <svx/algitem.hxx> +#include <svx/editobj.hxx> +#include <svx/editstat.hxx> +#include <svx/emphitem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/forbiddencharacterstable.hxx> +#include <svx/rotmodit.hxx> +#include <svx/scripttypeitem.hxx> +#include <svx/unolingu.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/broadcast.hxx> +#include <svtools/listeneriter.hxx> +#include <vcl/outdev.hxx> + +#include "column.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "attarray.hxx" +#include "patattr.hxx" +#include "cellform.hxx" +#include "collect.hxx" +#include "stlsheet.hxx" +#include "rechead.hxx" +#include "brdcst.hxx" +#include "editutil.hxx" +#include "subtotal.hxx" +#include "markdata.hxx" +#include "compiler.hxx" // ScTokenArray GetCodeLen +#include "dbcolect.hxx" +#include "fillinfo.hxx" + +#include <math.h> + +// ----------------------------------------------------------------------- + +// factor from font size to optimal cell height (text width) +#define SC_ROT_BREAK_FACTOR 6 + +// ----------------------------------------------------------------------- + +inline BOOL IsAmbiguousScript( BYTE nScript ) +{ + //! move to a header file + return ( nScript != SCRIPTTYPE_LATIN && + nScript != SCRIPTTYPE_ASIAN && + nScript != SCRIPTTYPE_COMPLEX ); +} + +// ----------------------------------------------------------------------------------------- + +// +// Datei-Operationen +// + +// ----------------------------------------------------------------------------------------- + +//UNUSED2008-05 SCROW ScColumn::NoteCount( SCROW nMaxRow ) const +//UNUSED2008-05 { +//UNUSED2008-05 SCROW nNoteCount = 0; +//UNUSED2008-05 SCSIZE i; +//UNUSED2008-05 +//UNUSED2008-05 for (i=0; i<nCount; i++) +//UNUSED2008-05 if ( pItems[i].pCell->GetNotePtr() && pItems[i].nRow<=nMaxRow ) +//UNUSED2008-05 ++nNoteCount; +//UNUSED2008-05 +//UNUSED2008-05 return nNoteCount; +//UNUSED2008-05 } + +// ----------------------------------------------------------------------------------------- + +//UNUSED2008-05 void ScColumn::CorrectSymbolCells( CharSet eStreamCharSet ) +//UNUSED2008-05 { +//UNUSED2008-05 // #99139# find and correct string cells that are formatted with a symbol font, +//UNUSED2008-05 // but are not in the LoadedSymbolStringCellsList +//UNUSED2008-05 // (because CELLTYPE_SYMBOLS wasn't written in the file) +//UNUSED2008-05 +//UNUSED2008-05 ScFontToSubsFontConverter_AutoPtr xFontConverter; +//UNUSED2008-05 const ULONG nFontConverterFlags = FONTTOSUBSFONT_EXPORT | FONTTOSUBSFONT_ONLYOLDSOSYMBOLFONTS; +//UNUSED2008-05 +//UNUSED2008-05 BOOL bListInitialized = FALSE; +//UNUSED2008-05 ScSymbolStringCellEntry* pCurrentEntry = NULL; +//UNUSED2008-05 +//UNUSED2008-05 ScAttrIterator aAttrIter( pAttrArray, 0, MAXROW ); +//UNUSED2008-05 SCROW nStt, nEnd; +//UNUSED2008-05 const ScPatternAttr* pAttr = aAttrIter.Next( nStt, nEnd ); +//UNUSED2008-05 while ( pAttr ) +//UNUSED2008-05 { +//UNUSED2008-05 if ( (xFontConverter = pAttr->GetSubsFontConverter( nFontConverterFlags )) || +//UNUSED2008-05 pAttr->IsSymbolFont() ) +//UNUSED2008-05 { +//UNUSED2008-05 ScColumnIterator aCellIter( this, nStt, nEnd ); +//UNUSED2008-05 SCROW nRow; +//UNUSED2008-05 ScBaseCell* pCell; +//UNUSED2008-05 while ( aCellIter.Next( nRow, pCell ) ) +//UNUSED2008-05 { +//UNUSED2008-05 if ( pCell->GetCellType() == CELLTYPE_STRING ) +//UNUSED2008-05 { +//UNUSED2008-05 List& rList = pDocument->GetLoadedSymbolStringCellsList(); +//UNUSED2008-05 if (!bListInitialized) +//UNUSED2008-05 { +//UNUSED2008-05 pCurrentEntry = (ScSymbolStringCellEntry*)rList.First(); +//UNUSED2008-05 bListInitialized = TRUE; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 while ( pCurrentEntry && pCurrentEntry->nRow < nRow ) +//UNUSED2008-05 pCurrentEntry = (ScSymbolStringCellEntry*)rList.Next(); +//UNUSED2008-05 +//UNUSED2008-05 if ( pCurrentEntry && pCurrentEntry->nRow == nRow ) +//UNUSED2008-05 { +//UNUSED2008-05 // found +//UNUSED2008-05 } +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 // not in list -> convert and put into list +//UNUSED2008-05 +//UNUSED2008-05 ScStringCell* pStrCell = (ScStringCell*)pCell; +//UNUSED2008-05 String aOldStr; +//UNUSED2008-05 pStrCell->GetString( aOldStr ); +//UNUSED2008-05 +//UNUSED2008-05 // convert back to stream character set (get original data) +//UNUSED2008-05 ByteString aByteStr( aOldStr, eStreamCharSet ); +//UNUSED2008-05 +//UNUSED2008-05 // convert using symbol encoding, as for CELLTYPE_SYMBOLS cells +//UNUSED2008-05 String aNewStr( aByteStr, RTL_TEXTENCODING_SYMBOL ); +//UNUSED2008-05 pStrCell->SetString( aNewStr ); +//UNUSED2008-05 +//UNUSED2008-05 ScSymbolStringCellEntry * pEntry = new ScSymbolStringCellEntry; +//UNUSED2008-05 pEntry->pCell = pStrCell; +//UNUSED2008-05 pEntry->nRow = nRow; +//UNUSED2008-05 +//UNUSED2008-05 if ( pCurrentEntry ) +//UNUSED2008-05 rList.Insert( pEntry ); // before current entry - pCurrentEntry stays valid +//UNUSED2008-05 else +//UNUSED2008-05 rList.Insert( pEntry, LIST_APPEND ); // append if already behind last entry +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 pAttr = aAttrIter.Next( nStt, nEnd ); +//UNUSED2008-05 } +//UNUSED2008-05 } + +// ----------------------------------------------------------------------------------------- + + // GetNeededSize: optimale Hoehe / Breite in Pixeln + +long ScColumn::GetNeededSize( SCROW nRow, OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bWidth, const ScNeededSizeOptions& rOptions ) +{ + long nValue=0; + SCSIZE nIndex; + double nPPT = bWidth ? nPPTX : nPPTY; + if (Search(nRow,nIndex)) + { + const ScPatternAttr* pPattern = rOptions.pPattern; + if (!pPattern) + pPattern = pAttrArray->GetPattern( nRow ); + + // zusammengefasst? + // Merge nicht in bedingter Formatierung + + const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG); + + if ( bWidth ) + { + if ( pFlag->IsHorOverlapped() ) + return 0; + if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 ) + return 0; + } + else + { + if ( pFlag->IsVerOverlapped() ) + return 0; + if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 ) + return 0; + } + + // bedingte Formatierung + const SfxItemSet* pCondSet = NULL; + if ( ((const SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ) + pCondSet = pDocument->GetCondResult( nCol, nRow, nTab ); + + // Zeilenumbruch? + + const SfxPoolItem* pCondItem; + SvxCellHorJustify eHorJust; + if (pCondSet && + pCondSet->GetItemState(ATTR_HOR_JUSTIFY, TRUE, &pCondItem) == SFX_ITEM_SET) + eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem*)pCondItem)->GetValue(); + else + eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) + pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue(); + BOOL bBreak; + if ( eHorJust == SVX_HOR_JUSTIFY_BLOCK ) + bBreak = TRUE; + else if ( pCondSet && + pCondSet->GetItemState(ATTR_LINEBREAK, TRUE, &pCondItem) == SFX_ITEM_SET) + bBreak = ((const SfxBoolItem*)pCondItem)->GetValue(); + else + bBreak = ((const SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue(); + + // get other attributes from pattern and conditional formatting + + SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet ); + BOOL bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED && + ((const SfxBoolItem&)pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() ); + if ( bAsianVertical ) + bBreak = FALSE; + + if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset) + return 0; + + long nRotate = 0; + SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD; + if ( eOrient == SVX_ORIENTATION_STANDARD ) + { + if (pCondSet && + pCondSet->GetItemState(ATTR_ROTATE_VALUE, TRUE, &pCondItem) == SFX_ITEM_SET) + nRotate = ((const SfxInt32Item*)pCondItem)->GetValue(); + else + nRotate = ((const SfxInt32Item&)pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue(); + if ( nRotate ) + { + if (pCondSet && + pCondSet->GetItemState(ATTR_ROTATE_MODE, TRUE, &pCondItem) == SFX_ITEM_SET) + eRotMode = (SvxRotateMode)((const SvxRotateModeItem*)pCondItem)->GetValue(); + else + eRotMode = (SvxRotateMode)((const SvxRotateModeItem&) + pPattern->GetItem(ATTR_ROTATE_MODE)).GetValue(); + + if ( nRotate == 18000 ) + eRotMode = SVX_ROTATE_MODE_STANDARD; // keinen Ueberlauf + } + } + + if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT ) + { + // ignore orientation/rotation if "repeat" is active + eOrient = SVX_ORIENTATION_STANDARD; + nRotate = 0; + bAsianVertical = FALSE; + } + + const SvxMarginItem* pMargin; + if (pCondSet && + pCondSet->GetItemState(ATTR_MARGIN, TRUE, &pCondItem) == SFX_ITEM_SET) + pMargin = (const SvxMarginItem*) pCondItem; + else + pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN); + USHORT nIndent = 0; + if ( eHorJust == SVX_HOR_JUSTIFY_LEFT ) + { + if (pCondSet && + pCondSet->GetItemState(ATTR_INDENT, TRUE, &pCondItem) == SFX_ITEM_SET) + nIndent = ((const SfxUInt16Item*)pCondItem)->GetValue(); + else + nIndent = ((const SfxUInt16Item&)pPattern->GetItem(ATTR_INDENT)).GetValue(); + } + + ScBaseCell* pCell = pItems[nIndex].pCell; + BYTE nScript = pDocument->GetScriptType( nCol, nRow, nTab, pCell ); + if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); + + // also call SetFont for edit cells, because bGetFont may be set only once + // bGetFont is set also if script type changes + if (rOptions.bGetFont) + { + Fraction aFontZoom = ( eOrient == SVX_ORIENTATION_STANDARD ) ? rZoomX : rZoomY; + Font aFont; + // font color doesn't matter here + pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript ); + pDev->SetFont(aFont); + } + + BOOL bAddMargin = TRUE; + CellType eCellType = pCell->GetCellType(); + + BOOL bEditEngine = ( eCellType == CELLTYPE_EDIT || + eOrient == SVX_ORIENTATION_STACKED || + IsAmbiguousScript( nScript ) || + ((eCellType == CELLTYPE_FORMULA) && ((ScFormulaCell*)pCell)->IsMultilineResult()) ); + + if (!bEditEngine) // direkte Ausgabe + { + String aValStr; + Color* pColor; + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + ULONG nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet ); + ScCellFormat::GetString( pCell, nFormat, aValStr, &pColor, + *pFormatter, + TRUE, rOptions.bFormula, ftCheck ); + if (aValStr.Len()) + { + // SetFont ist nach oben verschoben + + Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() ); + if ( eOrient != SVX_ORIENTATION_STANDARD ) + { + long nTemp = aSize.Width(); + aSize.Width() = aSize.Height(); + aSize.Height() = nTemp; + } + else if ( nRotate ) + { + //! unterschiedliche Skalierung X/Y beruecksichtigen + + double nRealOrient = nRotate * F_PI18000; // nRotate sind 1/100 Grad + double nCosAbs = fabs( cos( nRealOrient ) ); + double nSinAbs = fabs( sin( nRealOrient ) ); + long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); + long nWidth; + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); + else if ( rOptions.bTotalSize ) + { + nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); + bAddMargin = FALSE; + // nur nach rechts: + //! unterscheiden nach Ausrichtung oben/unten (nur Text/ganze Hoehe) + if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) + nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * + nPPT * nCosAbs / nSinAbs ); + } + else + nWidth = (long)( aSize.Height() / nSinAbs ); //! begrenzen? + + if ( bBreak && !rOptions.bTotalSize ) + { + // #47744# limit size for line break + long nCmp = pDev->GetFont().GetSize().Height() * SC_ROT_BREAK_FACTOR; + if ( nHeight > nCmp ) + nHeight = nCmp; + } + + aSize = Size( nWidth, nHeight ); + } + nValue = bWidth ? aSize.Width() : aSize.Height(); + + if ( bAddMargin ) + { + if (bWidth) + { + nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + + (long) ( pMargin->GetRightMargin() * nPPT ); + if ( nIndent ) + nValue += (long) ( nIndent * nPPT ); + } + else + nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + + (long) ( pMargin->GetBottomMargin() * nPPT ); + } + + // Zeilenumbruch ausgefuehrt ? + + if ( bBreak && !bWidth ) + { + // Test mit EditEngine zur Sicherheit schon bei 90% + // (wegen Rundungsfehlern und weil EditEngine teilweise anders formatiert) + + long nDocPixel = (long) ( ( pDocument->GetColWidth( nCol,nTab ) - + pMargin->GetLeftMargin() - pMargin->GetRightMargin() - + nIndent ) + * nPPT ); + nDocPixel = (nDocPixel * 9) / 10; // zur Sicherheit + if ( aSize.Width() > nDocPixel ) + bEditEngine = TRUE; + } + } + } + + if (bEditEngine) + { + // der Font wird bei !bEditEngine nicht jedesmal neu gesetzt + Font aOldFont = pDev->GetFont(); + + MapMode aHMMMode( MAP_100TH_MM, Point(), rZoomX, rZoomY ); + + // am Dokument speichern ? + ScFieldEditEngine* pEngine = pDocument->CreateFieldEditEngine(); + + pEngine->SetUpdateMode( FALSE ); + MapMode aOld = pDev->GetMapMode(); + pDev->SetMapMode( aHMMMode ); + pEngine->SetRefDevice( pDev ); + pEngine->SetForbiddenCharsTable( pDocument->GetForbiddenCharacters() ); + pEngine->SetAsianCompressionMode( pDocument->GetAsianCompression() ); + pEngine->SetKernAsianPunctuation( pDocument->GetAsianKerning() ); + SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pPattern->FillEditItemSet( pSet, pCondSet ); + +// no longer needed, are setted with the text (is faster) +// pEngine->SetDefaults( pSet ); + + if ( ((const SfxBoolItem&)pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) { + + com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() ); + pEngine->SetHyphenator( xXHyphenator ); + } + + Size aPaper = Size( 1000000, 1000000 ); + if ( eOrient==SVX_ORIENTATION_STACKED && !bAsianVertical ) + aPaper.Width() = 1; + else if (bBreak) + { + double fWidthFactor = nPPTX; + BOOL bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER ); + if ( bTextWysiwyg ) + { + // #95593# if text is formatted for printer, don't use PixelToLogic, + // to ensure the exact same paper width (and same line breaks) as in + // ScEditUtil::GetEditArea, used for output. + + fWidthFactor = HMM_PER_TWIPS; + } + + // use original width for hidden columns: + long nDocWidth = (long) ( pDocument->GetOriginalWidth(nCol,nTab) * fWidthFactor ); + SCCOL nColMerge = pMerge->GetColMerge(); + if (nColMerge > 1) + for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++) + nDocWidth += (long) ( pDocument->GetColWidth(nCol+nColAdd,nTab) * fWidthFactor ); + nDocWidth -= (long) ( pMargin->GetLeftMargin() * fWidthFactor ) + + (long) ( pMargin->GetRightMargin() * fWidthFactor ) + + 1; // Ausgabebereich ist Breite-1 Pixel (wegen Gitterlinien) + if ( nIndent ) + nDocWidth -= (long) ( nIndent * fWidthFactor ); + + // space for AutoFilter button: 20 * nZoom/100 + if ( pFlag->HasAutoFilter() && !bTextWysiwyg ) + nDocWidth -= (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); + + aPaper.Width() = nDocWidth; + + if ( !bTextWysiwyg ) + aPaper = pDev->PixelToLogic( aPaper, aHMMMode ); + } + pEngine->SetPaperSize(aPaper); + + if ( pCell->GetCellType() == CELLTYPE_EDIT ) + { + const EditTextObject* pData; + ((ScEditCell*)pCell)->GetData(pData); + pEngine->SetTextNewDefaults(*pData, pSet); + } + else + { + Color* pColor; + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + ULONG nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet ); + String aString; + ScCellFormat::GetString( pCell, nFormat, aString, &pColor, + *pFormatter, + TRUE, rOptions.bFormula, ftCheck ); + if (aString.Len()) + pEngine->SetTextNewDefaults(aString, pSet); + else + pEngine->SetDefaults(pSet); + } + + BOOL bEngineVertical = pEngine->IsVertical(); + pEngine->SetVertical( bAsianVertical ); + pEngine->SetUpdateMode( TRUE ); + + BOOL bEdWidth = bWidth; + if ( eOrient != SVX_ORIENTATION_STANDARD && eOrient != SVX_ORIENTATION_STACKED ) + bEdWidth = !bEdWidth; + if ( nRotate ) + { + //! unterschiedliche Skalierung X/Y beruecksichtigen + + Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() ); + double nRealOrient = nRotate * F_PI18000; // nRotate sind 1/100 Grad + double nCosAbs = fabs( cos( nRealOrient ) ); + double nSinAbs = fabs( sin( nRealOrient ) ); + long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs ); + long nWidth; + if ( eRotMode == SVX_ROTATE_MODE_STANDARD ) + nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs ); + else if ( rOptions.bTotalSize ) + { + nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT ); + bAddMargin = FALSE; + if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT ) + nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) * + nPPT * nCosAbs / nSinAbs ); + } + else + nWidth = (long)( aSize.Height() / nSinAbs ); //! begrenzen? + aSize = Size( nWidth, nHeight ); + + Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode ); + if ( bEdWidth ) + nValue = aPixSize.Width(); + else + { + nValue = aPixSize.Height(); + + if ( bBreak && !rOptions.bTotalSize ) + { + // #47744# limit size for line break + long nCmp = aOldFont.GetSize().Height() * SC_ROT_BREAK_FACTOR; + if ( nValue > nCmp ) + nValue = nCmp; + } + } + } + else if ( bEdWidth ) + { + if (bBreak) + nValue = 0; + else + nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ), + aHMMMode).Width(); + } + else // Hoehe + { + nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), + aHMMMode).Height(); + } + + if ( nValue && bAddMargin ) + { + if (bWidth) + { + nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) + + (long) ( pMargin->GetRightMargin() * nPPT ); + if (nIndent) + nValue += (long) ( nIndent * nPPT ); + } + else + { + nValue += (long) ( pMargin->GetTopMargin() * nPPT ) + + (long) ( pMargin->GetBottomMargin() * nPPT ); + + if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER ) + { + // add 1pt extra (default margin value) for line breaks with SetVertical + nValue += (long) ( 20 * nPPT ); + } + } + } + + // EditEngine is cached and re-used, so the old vertical flag must be restored + pEngine->SetVertical( bEngineVertical ); + + pDocument->DisposeFieldEditEngine(pEngine); + + pDev->SetMapMode( aOld ); + pDev->SetFont( aOldFont ); + } + + if (bWidth) + { + // Platz fuer Autofilter-Button + // 20 * nZoom/100 + // bedingte Formatierung hier nicht interessant + + INT16 nFlags = ((const ScMergeFlagAttr&)pPattern->GetItem(ATTR_MERGE_FLAG)).GetValue(); + if (nFlags & SC_MF_AUTO) + nValue += (rZoomX.GetNumerator()*20)/rZoomX.GetDenominator(); + } + } + return nValue; +} + +long ScColumn::GetSimpleTextNeededSize( SCSIZE nIndex, OutputDevice* pDev, + BOOL bWidth ) +{ + long nValue=0; + if ( nIndex < nCount ) + { + SCROW nRow = pItems[nIndex].nRow; + const ScPatternAttr* pPattern = pAttrArray->GetPattern( nRow ); + ScBaseCell* pCell = pItems[nIndex].pCell; + String aValStr; + Color* pColor; + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + ULONG nFormat = pPattern->GetNumberFormat( pFormatter ); + ScCellFormat::GetString( pCell, nFormat, aValStr, &pColor, + *pFormatter, TRUE, FALSE, ftCheck ); + if ( aValStr.Len() ) + { + if ( bWidth ) + nValue = pDev->GetTextWidth( aValStr ); + else + nValue = pDev->GetTextHeight(); + } + } + return nValue; +} + +USHORT ScColumn::GetOptimalColWidth( OutputDevice* pDev, double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bFormula, USHORT nOldWidth, + const ScMarkData* pMarkData, + BOOL bSimpleTextImport ) +{ + if (nCount == 0) + return nOldWidth; + + USHORT nWidth = (USHORT) (nOldWidth * nPPTX); + BOOL bFound = FALSE; + + SCSIZE nIndex; + ScMarkedDataIter aDataIter(this, pMarkData, TRUE); + if ( bSimpleTextImport ) + { // alles eins bis auf NumberFormate + const ScPatternAttr* pPattern = GetPattern( 0 ); + Font aFont; + // font color doesn't matter here + pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &rZoomX, NULL ); + pDev->SetFont( aFont ); + const SvxMarginItem* pMargin = (const SvxMarginItem*) &pPattern->GetItem(ATTR_MARGIN); + long nMargin = (long) ( pMargin->GetLeftMargin() * nPPTX ) + + (long) ( pMargin->GetRightMargin() * nPPTX ); + + while (aDataIter.Next( nIndex )) + { + USHORT nThis = (USHORT) (GetSimpleTextNeededSize( nIndex, pDev, + TRUE ) + nMargin); + if (nThis) + { + if (nThis>nWidth || !bFound) + { + nWidth = nThis; + bFound = TRUE; + } + } + } + } + else + { + ScNeededSizeOptions aOptions; + aOptions.bFormula = bFormula; + const ScPatternAttr* pOldPattern = NULL; + BYTE nOldScript = 0; + + while (aDataIter.Next( nIndex )) + { + SCROW nRow = pItems[nIndex].nRow; + + BYTE nScript = pDocument->GetScriptType( nCol, nRow, nTab, pItems[nIndex].pCell ); + if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType(); + + const ScPatternAttr* pPattern = GetPattern( nRow ); + aOptions.pPattern = pPattern; + aOptions.bGetFont = (pPattern != pOldPattern || nScript != nOldScript); + USHORT nThis = (USHORT) GetNeededSize( nRow, pDev, nPPTX, nPPTY, + rZoomX, rZoomY, TRUE, aOptions ); + pOldPattern = pPattern; + if (nThis) + { + if (nThis>nWidth || !bFound) + { + nWidth = nThis; + bFound = TRUE; + } + } + } + } + + if (bFound) + { + nWidth += 2; + USHORT nTwips = (USHORT) (nWidth / nPPTX); + return nTwips; + } + else + return nOldWidth; +} + +USHORT lcl_GetAttribHeight( const ScPatternAttr& rPattern, USHORT nFontHeightId ) +{ + USHORT nHeight = (USHORT) ((const SvxFontHeightItem&) rPattern.GetItem(nFontHeightId)).GetHeight(); + const SvxMarginItem* pMargin = (const SvxMarginItem*) &rPattern.GetItem(ATTR_MARGIN); + nHeight += nHeight / 5; + // gibt bei 10pt 240 + + if ( ((const SvxEmphasisMarkItem&)rPattern. + GetItem(ATTR_FONT_EMPHASISMARK)).GetEmphasisMark() != EMPHASISMARK_NONE ) + { + // add height for emphasis marks + //! font metrics should be used instead + nHeight += nHeight / 4; + } + + if ( nHeight + 240 > ScGlobal::nDefFontHeight ) + { + nHeight = sal::static_int_cast<USHORT>( nHeight + ScGlobal::nDefFontHeight ); + nHeight -= 240; + } + + // Standard-Hoehe: TextHeight + Raender - 23 + // -> 257 unter Windows + + if (nHeight > STD_ROWHEIGHT_DIFF) + nHeight -= STD_ROWHEIGHT_DIFF; + + nHeight += pMargin->GetTopMargin() + pMargin->GetBottomMargin(); + + return nHeight; +} + +// pHeight in Twips +// nMinHeight, nMinStart zur Optimierung: ab nRow >= nMinStart ist mindestens nMinHeight +// (wird nur bei bStdAllowed ausgewertet) + +void ScColumn::GetOptimalHeight( SCROW nStartRow, SCROW nEndRow, USHORT* pHeight, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bShrink, USHORT nMinHeight, SCROW nMinStart ) +{ + ScAttrIterator aIter( pAttrArray, nStartRow, nEndRow ); + + SCROW nStart; + SCROW nEnd; + SCROW nEditPos = 0; + SCROW nNextEnd = 0; + + // bei bedingter Formatierung werden immer die einzelnen Zellen angesehen + + const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd); + while ( pPattern ) + { + const ScMergeAttr* pMerge = (const ScMergeAttr*)&pPattern->GetItem(ATTR_MERGE); + const ScMergeFlagAttr* pFlag = (const ScMergeFlagAttr*)&pPattern->GetItem(ATTR_MERGE_FLAG); + if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() ) + { + // nix - vertikal bei der zusammengefassten und den ueberdeckten, + // horizontal nur bei den ueberdeckten (unsichtbaren) - + // eine nur horizontal zusammengefasste wird aber beruecksichtigt + } + else + { + SCROW nRow = 0; + BOOL bStdAllowed = (pPattern->GetCellOrientation() == SVX_ORIENTATION_STANDARD); + BOOL bStdOnly = FALSE; + if (bStdAllowed) + { + BOOL bBreak = ((SfxBoolItem&)pPattern->GetItem(ATTR_LINEBREAK)).GetValue() || + ((SvxCellHorJustify)((const SvxHorJustifyItem&)pPattern-> + GetItem( ATTR_HOR_JUSTIFY )).GetValue() == + SVX_HOR_JUSTIFY_BLOCK); + bStdOnly = !bBreak; + + // bedingte Formatierung: Zellen durchgehen + if ( bStdOnly && ((const SfxUInt32Item&)pPattern-> + GetItem(ATTR_CONDITIONAL)).GetValue() ) + bStdOnly = FALSE; + + // gedrehter Text: Zellen durchgehen + if ( bStdOnly && ((const SfxInt32Item&)pPattern-> + GetItem(ATTR_ROTATE_VALUE)).GetValue() ) + bStdOnly = FALSE; + } + + if (bStdOnly) + if (HasEditCells(nStart,nEnd,nEditPos)) // includes mixed script types + { + if (nEditPos == nStart) + { + bStdOnly = FALSE; + if (nEnd > nEditPos) + nNextEnd = nEnd; + nEnd = nEditPos; // einzeln ausrechnen + bStdAllowed = FALSE; // wird auf jeden Fall per Zelle berechnet + } + else + { + nNextEnd = nEnd; + nEnd = nEditPos - 1; // Standard - Teil + } + } + + if (bStdAllowed) + { + USHORT nLatHeight = 0; + USHORT nCjkHeight = 0; + USHORT nCtlHeight = 0; + USHORT nDefHeight; + BYTE nDefScript = ScGlobal::GetDefaultScriptType(); + if ( nDefScript == SCRIPTTYPE_ASIAN ) + nDefHeight = nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT ); + else if ( nDefScript == SCRIPTTYPE_COMPLEX ) + nDefHeight = nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT ); + else + nDefHeight = nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT ); + + // if everything below is already larger, the loop doesn't have to + // be run again + SCROW nStdEnd = nEnd; + if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart ) + nStdEnd = (nMinStart>0) ? nMinStart-1 : 0; + + for (nRow=nStart; nRow<=nStdEnd; nRow++) + if (nDefHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nDefHeight; + + if ( bStdOnly ) + { + // if cells are not handled individually below, + // check for cells with different script type + + SCSIZE nIndex; + Search(nStart,nIndex); + while ( nIndex < nCount && (nRow=pItems[nIndex].nRow) <= nEnd ) + { + BYTE nScript = pDocument->GetScriptType( nCol, nRow, nTab, pItems[nIndex].pCell ); + if ( nScript != nDefScript ) + { + if ( nScript == SCRIPTTYPE_ASIAN ) + { + if ( nCjkHeight == 0 ) + nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT ); + if (nCjkHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nCjkHeight; + } + else if ( nScript == SCRIPTTYPE_COMPLEX ) + { + if ( nCtlHeight == 0 ) + nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT ); + if (nCtlHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nCtlHeight; + } + else + { + if ( nLatHeight == 0 ) + nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT ); + if (nLatHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nLatHeight; + } + } + ++nIndex; + } + } + } + + if (!bStdOnly) // belegte Zellen suchen + { + ScNeededSizeOptions aOptions; + + SCSIZE nIndex; + Search(nStart,nIndex); + while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEnd) : FALSE ) + { + // Zellhoehe nur berechnen, wenn sie spaeter auch gebraucht wird (#37928#) + + if ( bShrink || !(pDocument->GetRowFlags(nRow, nTab) & CR_MANUALSIZE) ) + { + aOptions.pPattern = pPattern; + USHORT nHeight = (USHORT) + ( GetNeededSize( nRow, pDev, nPPTX, nPPTY, + rZoomX, rZoomY, FALSE, aOptions ) / nPPTY ); + if (nHeight > pHeight[nRow-nStartRow]) + pHeight[nRow-nStartRow] = nHeight; + } + ++nIndex; + } + } + } + + if (nNextEnd > 0) + { + nStart = nEnd + 1; + nEnd = nNextEnd; + nNextEnd = 0; + } + else + pPattern = aIter.Next(nStart,nEnd); + } +} + +BOOL ScColumn::GetNextSpellingCell(SCROW& nRow, BOOL bInSel, const ScMarkData& rData) const +{ + BOOL bStop = FALSE; + CellType eCellType; + SCSIZE nIndex; + if (!bInSel && Search(nRow, nIndex)) + { + eCellType = GetCellType(nRow); + if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && + pDocument->IsTabProtected(nTab)) ) + return TRUE; + } + while (!bStop) + { + if (bInSel) + { + nRow = rData.GetNextMarked(nCol, nRow, FALSE); + if (!ValidRow(nRow)) + { + nRow = MAXROW+1; + bStop = TRUE; + } + else + { + eCellType = GetCellType(nRow); + if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && + pDocument->IsTabProtected(nTab)) ) + return TRUE; + else + nRow++; + } + } + else if (GetNextDataPos(nRow)) + { + eCellType = GetCellType(nRow); + if ( (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) && + !(HasAttrib( nRow, nRow, HASATTR_PROTECTED) && + pDocument->IsTabProtected(nTab)) ) + return TRUE; + else + nRow++; + } + else + { + nRow = MAXROW+1; + bStop = TRUE; + } + } + return FALSE; +} + +// ========================================================================================= + +void ScColumn::RemoveAutoSpellObj() +{ + ScTabEditEngine* pEngine = NULL; + + for (SCSIZE i=0; i<nCount; i++) + if ( pItems[i].pCell->GetCellType() == CELLTYPE_EDIT ) + { + ScEditCell* pOldCell = (ScEditCell*) pItems[i].pCell; + const EditTextObject* pData = pOldCell->GetData(); + // keine Abfrage auf HasOnlineSpellErrors, damit es auch + // nach dem Laden funktioniert + + // Fuer den Test auf harte Formatierung (ScEditAttrTester) sind die Defaults + // in der EditEngine unwichtig. Wenn der Tester spaeter einmal gleiche + // Attribute in Default und harter Formatierung erkennen und weglassen sollte, + // muessten an der EditEngine zu jeder Zelle die richtigen Defaults gesetzt + // werden! + + // auf Attribute testen + if ( !pEngine ) + pEngine = new ScTabEditEngine(pDocument); + pEngine->SetText( *pData ); + ScEditAttrTester aTester( pEngine ); + if ( aTester.NeedsObject() ) // nur Spell-Errors entfernen + { + EditTextObject* pNewData = pEngine->CreateTextObject(); // ohne BIGOBJ + pOldCell->SetData( pNewData, pEngine->GetEditTextObjectPool() ); + delete pNewData; + } + else // String erzeugen + { + String aText = ScEditUtil::GetSpaceDelimitedString( *pEngine ); + ScBaseCell* pNewCell = new ScStringCell( aText ); + pNewCell->TakeBroadcaster( pOldCell->ReleaseBroadcaster() ); + pNewCell->TakeNote( pOldCell->ReleaseNote() ); + pItems[i].pCell = pNewCell; + delete pOldCell; + } + } + + delete pEngine; +} + +void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow ) +{ + ScFieldEditEngine* pEngine = NULL; + + SCSIZE i; + Search( nStartRow, i ); + for (; i<nCount && pItems[i].nRow <= nEndRow; i++) + if ( pItems[i].pCell->GetCellType() == CELLTYPE_EDIT ) + { + ScEditCell* pOldCell = (ScEditCell*) pItems[i].pCell; + const EditTextObject* pData = pOldCell->GetData(); + + // Fuer den Test auf harte Formatierung (ScEditAttrTester) sind die Defaults + // in der EditEngine unwichtig. Wenn der Tester spaeter einmal gleiche + // Attribute in Default und harter Formatierung erkennen und weglassen sollte, + // muessten an der EditEngine zu jeder Zelle die richtigen Defaults gesetzt + // werden! + + // auf Attribute testen + if ( !pEngine ) + { + //pEngine = new ScTabEditEngine(pDocument); + pEngine = new ScFieldEditEngine( pDocument->GetEditPool() ); + // EE_CNTRL_ONLINESPELLING falls schon Fehler drin sind + pEngine->SetControlWord( pEngine->GetControlWord() | EE_CNTRL_ONLINESPELLING ); + pEngine->SetForbiddenCharsTable( pDocument->GetForbiddenCharacters() ); + pEngine->SetAsianCompressionMode( pDocument->GetAsianCompression() ); + pEngine->SetKernAsianPunctuation( pDocument->GetAsianKerning() ); + } + pEngine->SetText( *pData ); + USHORT nParCount = pEngine->GetParagraphCount(); + for (USHORT nPar=0; nPar<nParCount; nPar++) + { + pEngine->QuickRemoveCharAttribs( nPar ); + const SfxItemSet& rOld = pEngine->GetParaAttribs( nPar ); + if ( rOld.Count() ) + { + SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // leer + pEngine->SetParaAttribs( nPar, aNew ); + } + } + // URL-Felder in Text wandeln (andere gibt's nicht, darum pType=0) + pEngine->RemoveFields( TRUE ); + + BOOL bSpellErrors = pEngine->HasOnlineSpellErrors(); + BOOL bNeedObject = bSpellErrors || nParCount>1; // Errors/Absaetze behalten + // ScEditAttrTester nicht mehr noetig, Felder sind raus + + if ( bNeedObject ) // bleibt Edit-Zelle + { + ULONG nCtrl = pEngine->GetControlWord(); + ULONG nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0; + if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig ) + pEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig ); + EditTextObject* pNewData = pEngine->CreateTextObject(); + pOldCell->SetData( pNewData, pEngine->GetEditTextObjectPool() ); + delete pNewData; + } + else // String erzeugen + { + String aText = ScEditUtil::GetSpaceDelimitedString( *pEngine ); + ScBaseCell* pNewCell = new ScStringCell( aText ); + pNewCell->TakeBroadcaster( pOldCell->ReleaseBroadcaster() ); + pNewCell->TakeNote( pOldCell->ReleaseNote() ); + pItems[i].pCell = pNewCell; + delete pOldCell; + } + } + + delete pEngine; +} + +// ========================================================================================= + +BOOL ScColumn::TestTabRefAbs(SCTAB nTable) +{ + BOOL bRet = FALSE; + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + if ( pItems[i].pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pItems[i].pCell)->TestTabRefAbs(nTable)) + bRet = TRUE; + return bRet; +} + +// ========================================================================================= + +ScColumnIterator::ScColumnIterator( const ScColumn* pCol, SCROW nStart, SCROW nEnd ) : + pColumn( pCol ), + nTop( nStart ), + nBottom( nEnd ) +{ + pColumn->Search( nTop, nPos ); +} + +ScColumnIterator::~ScColumnIterator() +{ +} + +BOOL ScColumnIterator::Next( SCROW& rRow, ScBaseCell*& rpCell ) +{ + if ( nPos < pColumn->nCount ) + { + rRow = pColumn->pItems[nPos].nRow; + if ( rRow <= nBottom ) + { + rpCell = pColumn->pItems[nPos].pCell; + ++nPos; + return TRUE; + } + } + + rRow = 0; + rpCell = NULL; + return FALSE; +} + +SCSIZE ScColumnIterator::GetIndex() const // Index zur letzen abgefragten Zelle +{ + return nPos - 1; // bei Next ist Pos hochgezaehlt worden +} + +// ----------------------------------------------------------------------------------------- + +ScMarkedDataIter::ScMarkedDataIter( const ScColumn* pCol, const ScMarkData* pMarkData, + BOOL bAllIfNone ) : + pColumn( pCol ), + pMarkIter( NULL ), + bNext( TRUE ), + bAll( bAllIfNone ) +{ + if (pMarkData && pMarkData->IsMultiMarked()) + pMarkIter = new ScMarkArrayIter( pMarkData->GetArray() + pCol->GetCol() ); +} + +ScMarkedDataIter::~ScMarkedDataIter() +{ + delete pMarkIter; +} + +BOOL ScMarkedDataIter::Next( SCSIZE& rIndex ) +{ + BOOL bFound = FALSE; + do + { + if (bNext) + { + if (!pMarkIter || !pMarkIter->Next( nTop, nBottom )) + { + if (bAll) // ganze Spalte + { + nTop = 0; + nBottom = MAXROW; + } + else + return FALSE; + } + pColumn->Search( nTop, nPos ); + bNext = FALSE; + bAll = FALSE; // nur beim ersten Versuch + } + + if ( nPos >= pColumn->nCount ) + return FALSE; + + if ( pColumn->pItems[nPos].nRow <= nBottom ) + bFound = TRUE; + else + bNext = TRUE; + } + while (!bFound); + + rIndex = nPos++; + return TRUE; +} + +//UNUSED2009-05 USHORT ScColumn::GetErrorData( SCROW nRow ) const +//UNUSED2009-05 { +//UNUSED2009-05 SCSIZE nIndex; +//UNUSED2009-05 if (Search(nRow, nIndex)) +//UNUSED2009-05 { +//UNUSED2009-05 ScBaseCell* pCell = pItems[nIndex].pCell; +//UNUSED2009-05 switch (pCell->GetCellType()) +//UNUSED2009-05 { +//UNUSED2009-05 case CELLTYPE_FORMULA : +//UNUSED2009-05 return ((ScFormulaCell*)pCell)->GetErrCode(); +//UNUSED2009-05 // break; +//UNUSED2009-05 default: +//UNUSED2009-05 return 0; +//UNUSED2009-05 } +//UNUSED2009-05 } +//UNUSED2009-05 return 0; +//UNUSED2009-05 } + +//------------ + +BOOL ScColumn::IsEmptyData() const +{ + return (nCount == 0); +} + +BOOL ScColumn::IsEmptyVisData(BOOL bNotes) const +{ + if (!pItems || nCount == 0) + return TRUE; + else + { + BOOL bVisData = FALSE; + SCSIZE i; + for (i=0; i<nCount && !bVisData; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) ) + bVisData = TRUE; + } + return !bVisData; + } +} + +SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const +{ + // Notizen werden nicht mitgezaehlt + + SCSIZE nVisCount = 0; + SCSIZE nIndex; + Search( nStartRow, nIndex ); + while ( nIndex < nCount && pItems[nIndex].nRow <= nEndRow ) + { + if ( pItems[nIndex].nRow >= nStartRow && + pItems[nIndex].pCell->GetCellType() != CELLTYPE_NOTE ) + { + ++nVisCount; + } + ++nIndex; + } + return nVisCount; +} + +SCROW ScColumn::GetLastVisDataPos(BOOL bNotes) const +{ + SCROW nRet = 0; + if (pItems) + { + SCSIZE i; + BOOL bFound = FALSE; + for (i=nCount; i>0 && !bFound; ) + { + --i; + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) ) + { + bFound = TRUE; + nRet = pItems[i].nRow; + } + } + } + return nRet; +} + +SCROW ScColumn::GetFirstVisDataPos(BOOL bNotes) const +{ + SCROW nRet = 0; + if (pItems) + { + SCSIZE i; + BOOL bFound = FALSE; + for (i=0; i<nCount && !bFound; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() != CELLTYPE_NOTE || (bNotes && pCell->HasNote()) ) + { + bFound = TRUE; + nRet = pItems[i].nRow; + } + } + } + return nRet; +} + +BOOL ScColumn::HasVisibleDataAt(SCROW nRow) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + if (!pItems[nIndex].pCell->IsBlank()) + return TRUE; + + return FALSE; +} + +BOOL ScColumn::IsEmptyAttr() const +{ + if (pAttrArray) + return pAttrArray->IsEmpty(); + else + return TRUE; +} + +BOOL ScColumn::IsEmpty() const +{ + return (IsEmptyData() && IsEmptyAttr()); +} + +BOOL ScColumn::IsEmptyBlock(SCROW nStartRow, SCROW nEndRow, bool bIgnoreNotes) const +{ + if ( nCount == 0 || !pItems ) + return TRUE; + + SCSIZE nIndex; + Search( nStartRow, nIndex ); + while ( nIndex < nCount && pItems[nIndex].nRow <= nEndRow ) + { + if ( !pItems[nIndex].pCell->IsBlank( bIgnoreNotes ) ) // found a cell + return FALSE; // not empty + ++nIndex; + } + return TRUE; // no cell found +} + +SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const +{ + SCSIZE nLines = 0; + BOOL bFound = FALSE; + SCSIZE i; + if (pItems && (nCount > 0)) + { + if (eDir == DIR_BOTTOM) + { + i = nCount; + while (!bFound && (i > 0)) + { + i--; + if ( pItems[i].nRow < nStartRow ) + break; + bFound = pItems[i].nRow <= nEndRow && !pItems[i].pCell->IsBlank(); + } + if (bFound) + nLines = static_cast<SCSIZE>(nEndRow - pItems[i].nRow); + else + nLines = static_cast<SCSIZE>(nEndRow - nStartRow); + } + else if (eDir == DIR_TOP) + { + i = 0; + while (!bFound && (i < nCount)) + { + if ( pItems[i].nRow > nEndRow ) + break; + bFound = pItems[i].nRow >= nStartRow && !pItems[i].pCell->IsBlank(); + i++; + } + if (bFound) + nLines = static_cast<SCSIZE>(pItems[i-1].nRow - nStartRow); + else + nLines = static_cast<SCSIZE>(nEndRow - nStartRow); + } + } + else + nLines = static_cast<SCSIZE>(nEndRow - nStartRow); + return nLines; +} + +//UNUSED2009-05 SCROW ScColumn::GetFirstDataPos() const +//UNUSED2009-05 { +//UNUSED2009-05 if (nCount) +//UNUSED2009-05 return pItems[0].nRow; +//UNUSED2009-05 else +//UNUSED2009-05 return 0; +//UNUSED2009-05 } + +SCROW ScColumn::GetLastDataPos() const +{ + if (nCount) + return pItems[nCount-1].nRow; + else + return 0; +} + +BOOL ScColumn::GetPrevDataPos(SCROW& rRow) const +{ + BOOL bFound = FALSE; + SCSIZE i = nCount; + while (!bFound && (i > 0)) + { + --i; + bFound = (pItems[i].nRow < rRow); + if (bFound) + rRow = pItems[i].nRow; + } + return bFound; +} + +BOOL ScColumn::GetNextDataPos(SCROW& rRow) const // groesser als rRow +{ + SCSIZE nIndex; + if (Search( rRow, nIndex )) + ++nIndex; // naechste Zelle + + BOOL bMore = ( nIndex < nCount ); + if ( bMore ) + rRow = pItems[nIndex].nRow; + return bMore; +} + +void ScColumn::FindDataAreaPos(SCROW& rRow, long nMovY) const +{ + if (!nMovY) return; + BOOL bForward = (nMovY>0); + + SCSIZE nIndex; + BOOL bThere = Search(rRow, nIndex); + if (bThere && pItems[nIndex].pCell->IsBlank()) + bThere = FALSE; + + if (bThere) + { + SCROW nLast = rRow; + SCSIZE nOldIndex = nIndex; + if (bForward) + { + if (nIndex<nCount-1) + { + ++nIndex; + while (nIndex<nCount-1 && pItems[nIndex].nRow==nLast+1 + && !pItems[nIndex].pCell->IsBlank()) + { + ++nIndex; + ++nLast; + } + if (nIndex==nCount-1) + if (pItems[nIndex].nRow==nLast+1 && !pItems[nIndex].pCell->IsBlank()) + ++nLast; + } + } + else + { + if (nIndex>0) + { + --nIndex; + while (nIndex>0 && pItems[nIndex].nRow+1==nLast + && !pItems[nIndex].pCell->IsBlank()) + { + --nIndex; + --nLast; + } + if (nIndex==0) + if (pItems[nIndex].nRow+1==nLast && !pItems[nIndex].pCell->IsBlank()) + --nLast; + } + } + if (nLast==rRow) + { + bThere = FALSE; + nIndex = bForward ? nOldIndex+1 : nOldIndex; + } + else + rRow = nLast; + } + + if (!bThere) + { + if (bForward) + { + while (nIndex<nCount && pItems[nIndex].pCell->IsBlank()) + ++nIndex; + if (nIndex<nCount) + rRow = pItems[nIndex].nRow; + else + rRow = MAXROW; + } + else + { + while (nIndex>0 && pItems[nIndex-1].pCell->IsBlank()) + --nIndex; + if (nIndex>0) + rRow = pItems[nIndex-1].nRow; + else + rRow = 0; + } + } +} + +BOOL ScColumn::HasDataAt(SCROW nRow) const +{ +/* SCSIZE nIndex; + return Search( nRow, nIndex ); +*/ + // immer nur sichtbare interessant ? + //! dann HasVisibleDataAt raus + + SCSIZE nIndex; + if (Search(nRow, nIndex)) + if (!pItems[nIndex].pCell->IsBlank()) + return TRUE; + + return FALSE; + +} + +BOOL ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const +{ + if (pAttrArray && rCol.pAttrArray) + return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow ); + else + return !pAttrArray && !rCol.pAttrArray; +} + +BOOL ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const +{ + if (pAttrArray && rCol.pAttrArray) + return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow ); + else + return !pAttrArray && !rCol.pAttrArray; +} + +BOOL ScColumn::GetFirstVisibleAttr( SCROW& rFirstRow ) const +{ + if (pAttrArray) + return pAttrArray->GetFirstVisibleAttr( rFirstRow ); + else + return FALSE; +} + +BOOL ScColumn::GetLastVisibleAttr( SCROW& rLastRow ) const +{ + if (pAttrArray) + { + // row of last cell is needed + SCROW nLastData = GetLastVisDataPos( TRUE ); // always including notes, 0 if none + + return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData ); + } + else + return FALSE; +} + +BOOL ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const +{ + if (pAttrArray) + return pAttrArray->HasVisibleAttrIn( nStartRow, nEndRow ); + else + return FALSE; +} + +void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, BOOL* pUsed ) const +{ + SCROW nRow = 0; + SCSIZE nIndex; + Search( nStartRow, nIndex ); + while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : FALSE ) + { + pUsed[nRow-nStartRow] = TRUE; + ++nIndex; + } +} + +void ScColumn::StartListening( SvtListener& rLst, SCROW nRow ) +{ + SvtBroadcaster* pBC = NULL; + ScBaseCell* pCell; + + SCSIZE nIndex; + if (Search(nRow,nIndex)) + { + pCell = pItems[nIndex].pCell; + pBC = pCell->GetBroadcaster(); + } + else + { + pCell = new ScNoteCell; + Insert(nRow, pCell); + } + + if (!pBC) + { + pBC = new SvtBroadcaster; + pCell->TakeBroadcaster(pBC); + } + rLst.StartListening(*pBC); +} + +void ScColumn::MoveListeners( SvtBroadcaster& rSource, SCROW nDestRow ) +{ + SvtBroadcaster* pBC = NULL; + ScBaseCell* pCell; + + SCSIZE nIndex; + if (Search(nDestRow,nIndex)) + { + pCell = pItems[nIndex].pCell; + pBC = pCell->GetBroadcaster(); + } + else + { + pCell = new ScNoteCell; + Insert(nDestRow, pCell); + } + + if (!pBC) + { + pBC = new SvtBroadcaster; + pCell->TakeBroadcaster(pBC); + } + + if (rSource.HasListeners()) + { + SvtListenerIter aIter( rSource); + for (SvtListener* pLst = aIter.GoStart(); pLst; pLst = aIter.GoNext()) + { + pLst->StartListening( *pBC); + pLst->EndListening( rSource); + } + } +} + +void ScColumn::EndListening( SvtListener& rLst, SCROW nRow ) +{ + SCSIZE nIndex; + if (Search(nRow,nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + SvtBroadcaster* pBC = pCell->GetBroadcaster(); + if (pBC) + { + rLst.EndListening(*pBC); + + if (!pBC->HasListeners()) + { + if (pCell->IsBlank()) + DeleteAtIndex(nIndex); + else + pCell->DeleteBroadcaster(); + } + } +// else +// DBG_ERROR("ScColumn::EndListening - kein Broadcaster"); + } +// else +// DBG_ERROR("ScColumn::EndListening - keine Zelle"); +} + +void ScColumn::CompileDBFormula() +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*) pCell)->CompileDBFormula(); + } +} + +void ScColumn::CompileDBFormula( BOOL bCreateFormulaString ) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*) pCell)->CompileDBFormula( bCreateFormulaString ); + } +} + +void ScColumn::CompileNameFormula( BOOL bCreateFormulaString ) +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*) pCell)->CompileNameFormula( bCreateFormulaString ); + } +} + +void ScColumn::CompileColRowNameFormula() +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*) pCell)->CompileColRowNameFormula(); + } +} + +void lcl_UpdateSubTotal( ScFunctionData& rData, ScBaseCell* pCell ) +{ + double nValue = 0.0; + BOOL bVal = FALSE; + BOOL bCell = TRUE; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + nValue = ((ScValueCell*)pCell)->GetValue(); + bVal = TRUE; + break; + case CELLTYPE_FORMULA: + { + if ( rData.eFunc != SUBTOTAL_FUNC_CNT2 ) // da interessiert's nicht + { + ScFormulaCell* pFC = (ScFormulaCell*)pCell; + if ( pFC->GetErrCode() ) + { + if ( rData.eFunc != SUBTOTAL_FUNC_CNT ) // fuer Anzahl einfach weglassen + rData.bError = TRUE; + } + else if (pFC->IsValue()) + { + nValue = pFC->GetValue(); + bVal = TRUE; + } + // sonst Text + } + } + break; + case CELLTYPE_NOTE: + bCell = FALSE; + break; + // bei Strings nichts + default: + { + // added to avoid warnings + } + } + + if (!rData.bError) + { + switch (rData.eFunc) + { + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_AVE: + if (bVal) + { + ++rData.nCount; + if (!SubTotal::SafePlus( rData.nVal, nValue )) + rData.bError = TRUE; + } + break; + case SUBTOTAL_FUNC_CNT: // nur Werte + if (bVal) + ++rData.nCount; + break; + case SUBTOTAL_FUNC_CNT2: // alle + if (bCell) + ++rData.nCount; + break; + case SUBTOTAL_FUNC_MAX: + if (bVal) + if (++rData.nCount == 1 || nValue > rData.nVal ) + rData.nVal = nValue; + break; + case SUBTOTAL_FUNC_MIN: + if (bVal) + if (++rData.nCount == 1 || nValue < rData.nVal ) + rData.nVal = nValue; + break; + default: + { + // added to avoid warnings + } + } + } +} + +// Mehrfachselektion: +void ScColumn::UpdateSelectionFunction( const ScMarkData& rMark, + ScFunctionData& rData, + const ScBitMaskCompressedArray< SCROW, BYTE>* pRowFlags, + BOOL bDoExclude, SCROW nExStartRow, SCROW nExEndRow ) +{ + SCSIZE nIndex; + ScMarkedDataIter aDataIter(this, &rMark, FALSE); + while (aDataIter.Next( nIndex )) + { + SCROW nRow = pItems[nIndex].nRow; + if ( !pRowFlags || !( pRowFlags->GetValue(nRow) & CR_HIDDEN ) ) + if ( !bDoExclude || nRow < nExStartRow || nRow > nExEndRow ) + lcl_UpdateSubTotal( rData, pItems[nIndex].pCell ); + } +} + +// bei bNoMarked die Mehrfachselektion weglassen +void ScColumn::UpdateAreaFunction( ScFunctionData& rData, + const ScBitMaskCompressedArray< SCROW, BYTE>* pRowFlags, + SCROW nStartRow, SCROW nEndRow ) +{ + SCSIZE nIndex; + Search( nStartRow, nIndex ); + while ( nIndex<nCount && pItems[nIndex].nRow<=nEndRow ) + { + SCROW nRow = pItems[nIndex].nRow; + if ( !pRowFlags || !( pRowFlags->GetValue(nRow) & CR_HIDDEN ) ) + lcl_UpdateSubTotal( rData, pItems[nIndex].pCell ); + ++nIndex; + } +} + +ULONG ScColumn::GetWeightedCount() const +{ + ULONG nTotal = 0; + + // Notizen werden nicht gezaehlt + + for (SCSIZE i=0; i<nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE: + case CELLTYPE_STRING: + ++nTotal; + break; + case CELLTYPE_FORMULA: + nTotal += 5 + ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen(); + break; + case CELLTYPE_EDIT: + nTotal += 50; + break; + default: + { + // added to avoid warnings + } + } + } + + return nTotal; +} + +ULONG ScColumn::GetCodeCount() const +{ + ULONG nCodeCount = 0; + + for (SCSIZE i=0; i<nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + nCodeCount += ((ScFormulaCell*)pCell)->GetCode()->GetCodeLen(); + } + + return nCodeCount; +} + + + + + diff --git a/sc/source/core/data/column3.cxx b/sc/source/core/data/column3.cxx new file mode 100644 index 000000000000..5726862366cf --- /dev/null +++ b/sc/source/core/data/column3.cxx @@ -0,0 +1,1926 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: column3.cxx,v $ + * $Revision: 1.27.128.7 $ + * + * 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 <sfx2/objsh.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/zformat.hxx> + +#include "scitems.hxx" +#include "column.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "attarray.hxx" +#include "patattr.hxx" +#include "cellform.hxx" +#include "collect.hxx" +#include "formula/errorcodes.hxx" +#include "formula/token.hxx" +#include "brdcst.hxx" +#include "docoptio.hxx" // GetStdPrecision fuer GetMaxNumberStringLen +#include "subtotal.hxx" +#include "markdata.hxx" +#include "detfunc.hxx" // fuer Notizen bei DeleteRange +#include "postit.hxx" + +// Err527 Workaround +extern const ScFormulaCell* pLastFormulaTreeTop; // in cellform.cxx +using namespace formula; +// STATIC DATA ----------------------------------------------------------- + +BOOL ScColumn::bDoubleAlloc = FALSE; // fuer Import: Groesse beim Allozieren verdoppeln + + +void ScColumn::Insert( SCROW nRow, ScBaseCell* pNewCell ) +{ + BOOL bIsAppended = FALSE; + if (pItems && nCount>0) + { + if (pItems[nCount-1].nRow < nRow) + { + Append(nRow, pNewCell ); + bIsAppended = TRUE; + } + } + if ( !bIsAppended ) + { + SCSIZE nIndex; + if (Search(nRow, nIndex)) + { + ScBaseCell* pOldCell = pItems[nIndex].pCell; + + // move broadcaster and note to new cell, if not existing in new cell + if (pOldCell->HasBroadcaster() && !pNewCell->HasBroadcaster()) + pNewCell->TakeBroadcaster( pOldCell->ReleaseBroadcaster() ); + if (pOldCell->HasNote() && !pNewCell->HasNote()) + pNewCell->TakeNote( pOldCell->ReleaseNote() ); + + if ( pOldCell->GetCellType() == CELLTYPE_FORMULA && !pDocument->IsClipOrUndo() ) + { + pOldCell->EndListeningTo( pDocument ); + // falls in EndListening NoteCell in gleicher Col zerstoert + if ( nIndex >= nCount || pItems[nIndex].nRow != nRow ) + Search(nRow, nIndex); + } + pOldCell->Delete(); + pItems[nIndex].pCell = pNewCell; + } + else + { + if (nCount + 1 > nLimit) + { + if (bDoubleAlloc) + { + if (nLimit < COLUMN_DELTA) + nLimit = COLUMN_DELTA; + else + { + nLimit *= 2; + if ( nLimit > sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + nLimit = MAXROWCOUNT; + } + } + else + nLimit += COLUMN_DELTA; + + ColEntry* pNewItems = new ColEntry[nLimit]; + if (pItems) + { + memmove( pNewItems, pItems, nCount * sizeof(ColEntry) ); + delete[] pItems; + } + pItems = pNewItems; + } + memmove( &pItems[nIndex + 1], &pItems[nIndex], (nCount - nIndex) * sizeof(ColEntry) ); + pItems[nIndex].pCell = pNewCell; + pItems[nIndex].nRow = nRow; + ++nCount; + } + } + // Bei aus Clipboard sind hier noch falsche (alte) Referenzen! + // Werden in CopyBlockFromClip per UpdateReference umgesetzt, + // danach StartListeningFromClip und BroadcastFromClip gerufen. + // Wird ins Clipboard/UndoDoc gestellt, wird kein Broadcast gebraucht. + // Nach Import wird CalcAfterLoad gerufen, dort Listening. + if ( !(pDocument->IsClipOrUndo() || pDocument->IsInsertingFromOtherDoc()) ) + { + pNewCell->StartListeningTo( pDocument ); + CellType eCellType = pNewCell->GetCellType(); + // Notizzelle entsteht beim Laden nur durch StartListeningCell, + // ausloesende Formelzelle muss sowieso dirty sein. + if ( !(pDocument->IsCalcingAfterLoad() && eCellType == CELLTYPE_NOTE) ) + { + if ( eCellType == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pNewCell)->SetDirty(); + else + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, + ScAddress( nCol, nRow, nTab ), pNewCell ) ); + } + } +} + + +void ScColumn::Insert( SCROW nRow, ULONG nNumberFormat, ScBaseCell* pCell ) +{ + Insert(nRow, pCell); + short eOldType = pDocument->GetFormatTable()-> + GetType( (ULONG) + ((SfxUInt32Item*)GetAttr( nRow, ATTR_VALUE_FORMAT ))-> + GetValue() ); + short eNewType = pDocument->GetFormatTable()->GetType(nNumberFormat); + if (!pDocument->GetFormatTable()->IsCompatible(eOldType, eNewType)) + ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT, (UINT32) nNumberFormat) ); +} + + +void ScColumn::Append( SCROW nRow, ScBaseCell* pCell ) +{ + if (nCount + 1 > nLimit) + { + if (bDoubleAlloc) + { + if (nLimit < COLUMN_DELTA) + nLimit = COLUMN_DELTA; + else + { + nLimit *= 2; + if ( nLimit > sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + nLimit = MAXROWCOUNT; + } + } + else + nLimit += COLUMN_DELTA; + + ColEntry* pNewItems = new ColEntry[nLimit]; + if (pItems) + { + memmove( pNewItems, pItems, nCount * sizeof(ColEntry) ); + delete[] pItems; + } + pItems = pNewItems; + } + pItems[nCount].pCell = pCell; + pItems[nCount].nRow = nRow; + ++nCount; +} + + +void ScColumn::Delete( SCROW nRow ) +{ + SCSIZE nIndex; + + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + ScNoteCell* pNoteCell = new ScNoteCell; + pItems[nIndex].pCell = pNoteCell; // Dummy fuer Interpret + pDocument->Broadcast( ScHint( SC_HINT_DYING, + ScAddress( nCol, nRow, nTab ), pCell ) ); + if ( SvtBroadcaster* pBC = pCell->ReleaseBroadcaster() ) + { + pNoteCell->TakeBroadcaster( pBC ); + } + else + { + delete pNoteCell; + --nCount; + memmove( &pItems[nIndex], &pItems[nIndex + 1], (nCount - nIndex) * sizeof(ColEntry) ); + pItems[nCount].nRow = 0; + pItems[nCount].pCell = NULL; + // Soll man hier den Speicher freigeben (delta)? Wird dann langsamer! + } + pCell->EndListeningTo( pDocument ); + pCell->Delete(); + } +} + + +void ScColumn::DeleteAtIndex( SCSIZE nIndex ) +{ + ScBaseCell* pCell = pItems[nIndex].pCell; + ScNoteCell* pNoteCell = new ScNoteCell; + pItems[nIndex].pCell = pNoteCell; // Dummy fuer Interpret + pDocument->Broadcast( ScHint( SC_HINT_DYING, + ScAddress( nCol, pItems[nIndex].nRow, nTab ), pCell ) ); + delete pNoteCell; + --nCount; + memmove( &pItems[nIndex], &pItems[nIndex + 1], (nCount - nIndex) * sizeof(ColEntry) ); + pItems[nCount].nRow = 0; + pItems[nCount].pCell = NULL; + pCell->EndListeningTo( pDocument ); + pCell->Delete(); +} + + +void ScColumn::FreeAll() +{ + if (pItems) + { + for (SCSIZE i = 0; i < nCount; i++) + pItems[i].pCell->Delete(); + delete[] pItems; + pItems = NULL; + } + nCount = 0; + nLimit = 0; +} + + +void ScColumn::DeleteRow( SCROW nStartRow, SCSIZE nSize ) +{ + pAttrArray->DeleteRow( nStartRow, nSize ); + + if ( !pItems || !nCount ) + return ; + + SCSIZE nFirstIndex; + Search( nStartRow, nFirstIndex ); + if ( nFirstIndex >= nCount ) + return ; + + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + BOOL bFound=FALSE; + SCROW nEndRow = nStartRow + nSize - 1; + SCSIZE nStartIndex = 0; + SCSIZE nEndIndex = 0; + SCSIZE i; + + for ( i = nFirstIndex; i < nCount && pItems[i].nRow <= nEndRow; i++ ) + { + if (!bFound) + { + nStartIndex = i; + bFound = TRUE; + } + nEndIndex = i; + + ScBaseCell* pCell = pItems[i].pCell; + SvtBroadcaster* pBC = pCell->GetBroadcaster(); + if (pBC) + { +// gibt jetzt invalid reference, kein Aufruecken der direkten Referenzen +// MoveListeners( *pBC, nRow+nSize ); + pCell->DeleteBroadcaster(); + // in DeleteRange werden leere Broadcaster geloescht + } + } + if (bFound) + { + DeleteRange( nStartIndex, nEndIndex, IDF_CONTENTS ); + Search( nStartRow, i ); + if ( i >= nCount ) + { + pDocument->SetAutoCalc( bOldAutoCalc ); + return ; + } + } + else + i = nFirstIndex; + + ScAddress aAdr( nCol, 0, nTab ); + ScHint aHint( SC_HINT_DATACHANGED, aAdr, NULL ); // only areas (ScBaseCell* == NULL) + ScAddress& rAddress = aHint.GetAddress(); + // for sparse occupation use single broadcasts, not ranges + BOOL bSingleBroadcasts = (((pItems[nCount-1].nRow - pItems[i].nRow) / + (nCount - i)) > 1); + if ( bSingleBroadcasts ) + { + SCROW nLastBroadcast = MAXROW+1; + for ( ; i < nCount; i++ ) + { + SCROW nOldRow = pItems[i].nRow; + // #43940# Aenderung Quelle broadcasten + rAddress.SetRow( nOldRow ); + pDocument->AreaBroadcast( aHint ); + SCROW nNewRow = (pItems[i].nRow -= nSize); + // #43940# Aenderung Ziel broadcasten + if ( nLastBroadcast != nNewRow ) + { // direkt aufeinanderfolgende nicht doppelt broadcasten + rAddress.SetRow( nNewRow ); + pDocument->AreaBroadcast( aHint ); + } + nLastBroadcast = nOldRow; + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + } + } + else + { + rAddress.SetRow( pItems[i].nRow ); + ScRange aRange( rAddress ); + aRange.aEnd.SetRow( pItems[nCount-1].nRow ); + for ( ; i < nCount; i++ ) + { + SCROW nNewRow = (pItems[i].nRow -= nSize); + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->aPos.SetRow( nNewRow ); + } + pDocument->AreaBroadcastInRange( aRange, aHint ); + } + + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScColumn::DeleteRange( SCSIZE nStartIndex, SCSIZE nEndIndex, USHORT nDelFlag ) +{ + /* If caller specifies to not remove the note caption objects, all cells + have to forget the pointers to them. This is used e.g. while undoing a + "paste cells" operation, which removes the caption objects later in + drawing undo. */ + bool bDeleteNote = (nDelFlag & IDF_NOTE) != 0; + bool bNoCaptions = (nDelFlag & IDF_NOCAPTIONS) != 0; + if (bDeleteNote && bNoCaptions) + for ( SCSIZE nIdx = nStartIndex; nIdx <= nEndIndex; ++nIdx ) + if ( ScPostIt* pNote = pItems[ nIdx ].pCell->GetNote() ) + pNote->ForgetCaption(); + + // special simple mode if all contents are deleted and cells do not contain broadcasters + bool bSimple = ((nDelFlag & IDF_CONTENTS) == IDF_CONTENTS); + if (bSimple) + for ( SCSIZE nIdx = nStartIndex; bSimple && (nIdx <= nEndIndex); ++nIdx ) + if (pItems[ nIdx ].pCell->GetBroadcaster()) + bSimple = false; + + ScHint aHint( SC_HINT_DYING, ScAddress( nCol, 0, nTab ), 0 ); + + // cache all formula cells, they will be deleted at end of this function + typedef ::std::vector< ScFormulaCell* > FormulaCellVector; + FormulaCellVector aDelCells; + aDelCells.reserve( nEndIndex - nStartIndex + 1 ); + + // simple deletion of the cell objects + if (bSimple) + { + // pNoteCell: dummy replacement for old cells, to prevent that interpreter uses old cell + ScNoteCell* pNoteCell = new ScNoteCell; + for ( SCSIZE nIdx = nStartIndex; nIdx <= nEndIndex; ++nIdx ) + { + ScBaseCell* pOldCell = pItems[ nIdx ].pCell; + if (pOldCell->GetCellType() == CELLTYPE_FORMULA) + { + // cache formula cell, will be deleted below + aDelCells.push_back( static_cast< ScFormulaCell* >( pOldCell ) ); + } + else + { + // interpret in broadcast must not use the old cell + pItems[ nIdx ].pCell = pNoteCell; + aHint.GetAddress().SetRow( pItems[ nIdx ].nRow ); + aHint.SetCell( pOldCell ); + pDocument->Broadcast( aHint ); + pOldCell->Delete(); + } + } + delete pNoteCell; + memmove( &pItems[nStartIndex], &pItems[nEndIndex + 1], (nCount - nEndIndex - 1) * sizeof(ColEntry) ); + nCount -= nEndIndex-nStartIndex+1; + } + + // else: delete some contents of the cells + else + { + SCSIZE j = nStartIndex; + for ( SCSIZE nIdx = nStartIndex; nIdx <= nEndIndex; ++nIdx ) + { + // decide whether to delete the cell object according to passed flags + bool bDelete = false; + ScBaseCell* pOldCell = pItems[j].pCell; + CellType eCellType = pOldCell->GetCellType(); + switch ( eCellType ) + { + case CELLTYPE_VALUE: + { + USHORT nValFlags = nDelFlag & (IDF_DATETIME|IDF_VALUE); + // delete values and dates? + bDelete = nValFlags == (IDF_DATETIME|IDF_VALUE); + // if not, decide according to cell number format + if( !bDelete && (nValFlags != 0) ) + { + ULONG nIndex = (ULONG)((SfxUInt32Item*)GetAttr( pItems[j].nRow, ATTR_VALUE_FORMAT ))->GetValue(); + short nType = pDocument->GetFormatTable()->GetType(nIndex); + bool bIsDate = (nType == NUMBERFORMAT_DATE) || (nType == NUMBERFORMAT_TIME) || (nType == NUMBERFORMAT_DATETIME); + bDelete = nValFlags == (bIsDate ? IDF_DATETIME : IDF_VALUE); + } + } + break; + + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + bDelete = (nDelFlag & IDF_STRING) != 0; + break; + + case CELLTYPE_FORMULA: + bDelete = (nDelFlag & IDF_FORMULA) != 0; + break; + + case CELLTYPE_NOTE: + // do note delete note cell with broadcaster + bDelete = bDeleteNote && !pOldCell->GetBroadcaster(); + break; + + default:; // added to avoid warnings + } + + if (bDelete) + { + // try to create a replacement note cell, if note or broadcaster exists + ScNoteCell* pNoteCell = 0; + if (eCellType != CELLTYPE_NOTE) + { + // do not rescue note if it has to be deleted according to passed flags + ScPostIt* pNote = bDeleteNote ? 0 : pOldCell->ReleaseNote(); + // #i99844# do not release broadcaster from old cell, it still has to notify deleted content + SvtBroadcaster* pBC = pOldCell->GetBroadcaster(); + if( pNote || pBC ) + pNoteCell = new ScNoteCell( pNote, pBC ); + } + + // remove cell entry in cell item list + SCROW nOldRow = pItems[j].nRow; + if (pNoteCell) + { + // replace old cell with the replacement note cell + pItems[j].pCell = pNoteCell; + ++j; + } + else + { + // remove the old cell from the cell item list + --nCount; + memmove( &pItems[j], &pItems[j + 1], (nCount - j) * sizeof(ColEntry) ); + pItems[nCount].nRow = 0; + pItems[nCount].pCell = 0; + } + + // cache formula cells (will be deleted later), delete cell of other type + if (eCellType == CELLTYPE_FORMULA) + { + aDelCells.push_back( static_cast< ScFormulaCell* >( pOldCell ) ); + } + else + { + aHint.GetAddress().SetRow( nOldRow ); + aHint.SetCell( pOldCell ); + pDocument->Broadcast( aHint ); + // #i99844# after broadcasting, old cell has to forget the broadcaster (owned by pNoteCell) + pOldCell->ReleaseBroadcaster(); + pOldCell->Delete(); + } + } + else + { + // delete cell note + if (bDeleteNote) + pItems[j].pCell->DeleteNote(); + // cell not deleted, move index to next cell + ++j; + } + } + } + + // *** delete all formula cells *** + + // first, all cells stop listening, may save unneeded recalcualtions + for ( FormulaCellVector::iterator aIt = aDelCells.begin(), aEnd = aDelCells.end(); aIt != aEnd; ++aIt ) + (*aIt)->EndListeningTo( pDocument ); + + // #i101869# if the note cell with the broadcaster was deleted in EndListening, + // forget the pointer to the broadcaster + for ( FormulaCellVector::iterator aIt = aDelCells.begin(), aEnd = aDelCells.end(); aIt != aEnd; ++aIt ) + { + SCSIZE nIndex; + if ( !Search( (*aIt)->aPos.Row(), nIndex ) ) + (*aIt)->ReleaseBroadcaster(); + } + + // broadcast SC_HINT_DYING for all cells and delete them + for ( FormulaCellVector::iterator aIt = aDelCells.begin(), aEnd = aDelCells.end(); aIt != aEnd; ++aIt ) + { + aHint.SetAddress( (*aIt)->aPos ); + aHint.SetCell( *aIt ); + pDocument->Broadcast( aHint ); + // #i99844# after broadcasting, old cell has to forget the broadcaster (owned by replacement note cell) + (*aIt)->ReleaseBroadcaster(); + (*aIt)->Delete(); + } +} + + +void ScColumn::DeleteArea(SCROW nStartRow, SCROW nEndRow, USHORT nDelFlag) +{ + // FreeAll darf hier nicht gerufen werden wegen Broadcastern + + // Attribute erst am Ende, damit vorher noch zwischen Zahlen und Datum + // unterschieden werden kann (#47901#) + + USHORT nContMask = IDF_CONTENTS; + // IDF_NOCAPTIONS needs to be passed too, if IDF_NOTE is set + if( nDelFlag & IDF_NOTE ) + nContMask |= IDF_NOCAPTIONS; + USHORT nContFlag = nDelFlag & nContMask; + + if (pItems && nCount>0 && nContFlag) + { + if (nStartRow==0 && nEndRow==MAXROW) + DeleteRange( 0, nCount-1, nContFlag ); + else + { + BOOL bFound=FALSE; + SCSIZE nStartIndex = 0; + SCSIZE nEndIndex = 0; + for (SCSIZE i = 0; i < nCount; i++) + if ((pItems[i].nRow >= nStartRow) && (pItems[i].nRow <= nEndRow)) + { + if (!bFound) + { + nStartIndex = i; + bFound = TRUE; + } + nEndIndex = i; + } + if (bFound) + DeleteRange( nStartIndex, nEndIndex, nContFlag ); + } + } + + if ( nDelFlag & IDF_EDITATTR ) + { + DBG_ASSERT( nContFlag == 0, "DeleteArea: falsche Flags" ); + RemoveEditAttribs( nStartRow, nEndRow ); + } + + // Attribute erst hier + if ((nDelFlag & IDF_ATTRIB) == IDF_ATTRIB) pAttrArray->DeleteArea( nStartRow, nEndRow ); + else if ((nDelFlag & IDF_ATTRIB) != 0) pAttrArray->DeleteHardAttr( nStartRow, nEndRow ); +} + + +ScFormulaCell* ScColumn::CreateRefCell( ScDocument* pDestDoc, const ScAddress& rDestPos, + SCSIZE nIndex, USHORT nFlags ) const +{ + USHORT nContFlags = nFlags & IDF_CONTENTS; + if (!nContFlags) + return NULL; + + // Testen, ob Zelle kopiert werden soll + // auch bei IDF_CONTENTS komplett, wegen Notes / Broadcastern + + BOOL bMatch = FALSE; + ScBaseCell* pCell = pItems[nIndex].pCell; + CellType eCellType = pCell->GetCellType(); + switch ( eCellType ) + { + case CELLTYPE_VALUE: + { + USHORT nValFlags = nFlags & (IDF_DATETIME|IDF_VALUE); + + if ( nValFlags == (IDF_DATETIME|IDF_VALUE) ) + bMatch = TRUE; + else if ( nValFlags ) + { + ULONG nNumIndex = (ULONG)((SfxUInt32Item*)GetAttr( + pItems[nIndex].nRow, ATTR_VALUE_FORMAT ))->GetValue(); + short nTyp = pDocument->GetFormatTable()->GetType(nNumIndex); + if ((nTyp == NUMBERFORMAT_DATE) || (nTyp == NUMBERFORMAT_TIME) || (nTyp == NUMBERFORMAT_DATETIME)) + bMatch = ((nFlags & IDF_DATETIME) != 0); + else + bMatch = ((nFlags & IDF_VALUE) != 0); + } + } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: bMatch = ((nFlags & IDF_STRING) != 0); break; + case CELLTYPE_FORMULA: bMatch = ((nFlags & IDF_FORMULA) != 0); break; + default: + { + // added to avoid warnings + } + } + if (!bMatch) + return NULL; + + + // Referenz einsetzen + ScSingleRefData aRef; + aRef.nCol = nCol; + aRef.nRow = pItems[nIndex].nRow; + aRef.nTab = nTab; + aRef.InitFlags(); // -> alles absolut + aRef.SetFlag3D(TRUE); + + //! 3D(FALSE) und TabRel(TRUE), wenn die endgueltige Position auf der selben Tabelle ist? + //! (bei TransposeClip ist die Zielposition noch nicht bekannt) + + aRef.CalcRelFromAbs( rDestPos ); + + ScTokenArray aArr; + aArr.AddSingleReference( aRef ); + + return new ScFormulaCell( pDestDoc, rDestPos, &aArr ); +} + + +// rColumn = Quelle +// nRow1, nRow2 = Zielposition + +void ScColumn::CopyFromClip(SCROW nRow1, SCROW nRow2, long nDy, + USHORT nInsFlag, BOOL bAsLink, BOOL bSkipAttrForEmpty, + ScColumn& rColumn) +{ + if ((nInsFlag & IDF_ATTRIB) != 0) + { + if ( bSkipAttrForEmpty ) + { + // copy only attributes for non-empty cells + // (notes are not counted as non-empty here, to match the content behavior) + + SCSIZE nStartIndex; + rColumn.Search( nRow1-nDy, nStartIndex ); + while ( nStartIndex < rColumn.nCount && rColumn.pItems[nStartIndex].nRow <= nRow2-nDy ) + { + SCSIZE nEndIndex = nStartIndex; + if ( rColumn.pItems[nStartIndex].pCell->GetCellType() != CELLTYPE_NOTE ) + { + SCROW nStartRow = rColumn.pItems[nStartIndex].nRow; + SCROW nEndRow = nStartRow; + + // find consecutive non-empty cells + + while ( nEndRow < nRow2-nDy && + nEndIndex+1 < rColumn.nCount && + rColumn.pItems[nEndIndex+1].nRow == nEndRow+1 && + rColumn.pItems[nEndIndex+1].pCell->GetCellType() != CELLTYPE_NOTE ) + { + ++nEndIndex; + ++nEndRow; + } + + rColumn.pAttrArray->CopyAreaSafe( nStartRow+nDy, nEndRow+nDy, nDy, *pAttrArray ); + } + nStartIndex = nEndIndex + 1; + } + } + else + rColumn.pAttrArray->CopyAreaSafe( nRow1, nRow2, nDy, *pAttrArray ); + } + if ((nInsFlag & IDF_CONTENTS) == 0) + return; + + if ( bAsLink && nInsFlag == IDF_ALL ) + { + // bei "alles" werden auch leere Zellen referenziert + //! IDF_ALL muss immer mehr Flags enthalten, als bei "Inhalte Einfuegen" + //! einzeln ausgewaehlt werden koennen! + + Resize( nCount + static_cast<SCSIZE>(nRow2-nRow1+1) ); + + ScAddress aDestPos( nCol, 0, nTab ); // Row wird angepasst + + // Referenz erzeugen (Quell-Position) + ScSingleRefData aRef; + aRef.nCol = rColumn.nCol; + // nRow wird angepasst + aRef.nTab = rColumn.nTab; + aRef.InitFlags(); // -> alles absolut + aRef.SetFlag3D(TRUE); + + for (SCROW nDestRow = nRow1; nDestRow <= nRow2; nDestRow++) + { + aRef.nRow = nDestRow - nDy; // Quell-Zeile + aDestPos.SetRow( nDestRow ); + + aRef.CalcRelFromAbs( aDestPos ); + ScTokenArray aArr; + aArr.AddSingleReference( aRef ); + Insert( nDestRow, new ScFormulaCell( pDocument, aDestPos, &aArr ) ); + } + + return; + } + + SCSIZE nColCount = rColumn.nCount; + + // ignore IDF_FORMULA - "all contents but no formulas" results in the same number of cells + if ((nInsFlag & ( IDF_CONTENTS & ~IDF_FORMULA )) == ( IDF_CONTENTS & ~IDF_FORMULA ) && nRow2-nRow1 >= 64) + { + //! Always do the Resize from the outside, where the number of repetitions is known + //! (then it can be removed here) + + SCSIZE nNew = nCount + nColCount; + if ( nLimit < nNew ) + Resize( nNew ); + } + + // IDF_ADDNOTES must be passed without other content flags than IDF_NOTE + bool bAddNotes = (nInsFlag & (IDF_CONTENTS | IDF_ADDNOTES)) == (IDF_NOTE | IDF_ADDNOTES); + + BOOL bAtEnd = FALSE; + for (SCSIZE i = 0; i < nColCount && !bAtEnd; i++) + { + SCsROW nDestRow = rColumn.pItems[i].nRow + nDy; + if ( nDestRow > (SCsROW) nRow2 ) + bAtEnd = TRUE; + else if ( nDestRow >= (SCsROW) nRow1 ) + { + // rows at the beginning may be skipped if filtered rows are left out, + // nDestRow may be negative then + + ScAddress aDestPos( nCol, (SCROW)nDestRow, nTab ); + + /* #i102056# Paste from clipboard needs to paste the cell notes in + a second pass. This must not overwrite the existing cells + already copied to the destination position in the first pass. + To indicate this special case, the modifier IDF_ADDNOTES is + passed together with IDF_NOTE in nInsFlag. Of course, there is + still the need to create a new cell, if there is no cell at the + destination position at all. */ + ScBaseCell* pAddNoteCell = bAddNotes ? GetCell( aDestPos.Row() ) : 0; + if (pAddNoteCell) + { + // do nothing if source cell does not contain a note + const ScBaseCell* pSourceCell = rColumn.pItems[i].pCell; + const ScPostIt* pSourceNote = pSourceCell ? pSourceCell->GetNote() : 0; + if (pSourceNote) + { + DBG_ASSERT( !pAddNoteCell->HasNote(), "ScColumn::CopyFromClip - unexpected note at destination cell" ); + bool bCloneCaption = (nInsFlag & IDF_NOCAPTIONS) == 0; + // #i52342# if caption is cloned, the note must be constructed with the destination document + ScAddress aSourcePos( rColumn.nCol, rColumn.pItems[i].nRow, rColumn.nTab ); + ScPostIt* pNewNote = pSourceNote->Clone( aSourcePos, *pDocument, aDestPos, bCloneCaption ); + pAddNoteCell->TakeNote( pNewNote ); + } + } + else + { + ScBaseCell* pNewCell = bAsLink ? + rColumn.CreateRefCell( pDocument, aDestPos, i, nInsFlag ) : + rColumn.CloneCell( i, nInsFlag, *pDocument, aDestPos ); + if (pNewCell) + Insert( aDestPos.Row(), pNewCell ); + } + } + } +} + + +namespace { + +/** Helper for ScColumn::CloneCell - decides whether to clone a value cell depending on clone flags and number format. */ +bool lclCanCloneValue( ScDocument& rDoc, const ScColumn& rCol, SCROW nRow, bool bCloneValue, bool bCloneDateTime ) +{ + // values and dates, or nothing to be cloned -> not needed to check number format + if( bCloneValue == bCloneDateTime ) + return bCloneValue; + + // check number format of value cell + ULONG nNumIndex = (ULONG)((SfxUInt32Item*)rCol.GetAttr( nRow, ATTR_VALUE_FORMAT ))->GetValue(); + short nTyp = rDoc.GetFormatTable()->GetType( nNumIndex ); + bool bIsDateTime = (nTyp == NUMBERFORMAT_DATE) || (nTyp == NUMBERFORMAT_TIME) || (nTyp == NUMBERFORMAT_DATETIME); + return bIsDateTime ? bCloneDateTime : bCloneValue; +} + +} // namespace + + +ScBaseCell* ScColumn::CloneCell(SCSIZE nIndex, USHORT nFlags, ScDocument& rDestDoc, const ScAddress& rDestPos) +{ + bool bCloneValue = (nFlags & IDF_VALUE) != 0; + bool bCloneDateTime = (nFlags & IDF_DATETIME) != 0; + bool bCloneString = (nFlags & IDF_STRING) != 0; + bool bCloneFormula = (nFlags & IDF_FORMULA) != 0; + bool bCloneNote = (nFlags & IDF_NOTE) != 0; + + ScBaseCell* pNew = 0; + ScBaseCell& rSource = *pItems[nIndex].pCell; + switch (rSource.GetCellType()) + { + case CELLTYPE_NOTE: + // note will be cloned below + break; + + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + // note will be cloned below + if (bCloneString) + pNew = rSource.CloneWithoutNote( rDestDoc, rDestPos ); + break; + + case CELLTYPE_VALUE: + // note will be cloned below + if (lclCanCloneValue( *pDocument, *this, pItems[nIndex].nRow, bCloneValue, bCloneDateTime )) + pNew = rSource.CloneWithoutNote( rDestDoc, rDestPos ); + break; + + case CELLTYPE_FORMULA: + if (bCloneFormula) + { + // note will be cloned below + pNew = rSource.CloneWithoutNote( rDestDoc, rDestPos ); + } + else if ( (bCloneValue || bCloneDateTime || bCloneString) && !rDestDoc.IsUndo() ) + { + // #48491# ins Undo-Dokument immer nur die Original-Zelle kopieren, + // aus Formeln keine Value/String-Zellen erzeugen + ScFormulaCell& rForm = (ScFormulaCell&)rSource; + USHORT nErr = rForm.GetErrCode(); + if ( nErr ) + { + // error codes are cloned with values + if (bCloneValue) + { + ScFormulaCell* pErrCell = new ScFormulaCell( &rDestDoc, rDestPos ); + pErrCell->SetErrCode( nErr ); + pNew = pErrCell; + } + } + else if (rForm.IsValue()) + { + if (lclCanCloneValue( *pDocument, *this, pItems[nIndex].nRow, bCloneValue, bCloneDateTime )) + { + double nVal = rForm.GetValue(); + pNew = new ScValueCell(nVal); + } + } + else if (bCloneString) + { + String aString; + rForm.GetString( aString ); + // #33224# do not clone empty string + if (aString.Len() > 0) + { + if ( rForm.IsMultilineResult() ) + { + pNew = new ScEditCell( aString, &rDestDoc ); + } + else + { + pNew = new ScStringCell( aString ); + } + } + } + } + break; + + default: DBG_ERRORFILE( "ScColumn::CloneCell - unknown cell type" ); + } + + // clone the cell note + if (bCloneNote) + { + if (ScPostIt* pNote = rSource.GetNote()) + { + bool bCloneCaption = (nFlags & IDF_NOCAPTIONS) == 0; + // #i52342# if caption is cloned, the note must be constructed with the destination document + ScAddress aOwnPos( nCol, pItems[nIndex].nRow, nTab ); + ScPostIt* pNewNote = pNote->Clone( aOwnPos, rDestDoc, rDestPos, bCloneCaption ); + if (!pNew) + pNew = new ScNoteCell( pNewNote ); + else + pNew->TakeNote( pNewNote ); + } + } + + return pNew; +} + + +void ScColumn::MixMarked( const ScMarkData& rMark, USHORT nFunction, + BOOL bSkipEmpty, ScColumn& rSrcCol ) +{ + SCROW nRow1, nRow2; + + if (rMark.IsMultiMarked()) + { + ScMarkArrayIter aIter( rMark.GetArray()+nCol ); + while (aIter.Next( nRow1, nRow2 )) + MixData( nRow1, nRow2, nFunction, bSkipEmpty, rSrcCol ); + } +} + + +// Ergebnis in rVal1 + +BOOL lcl_DoFunction( double& rVal1, double nVal2, USHORT nFunction ) +{ + BOOL bOk = FALSE; + switch (nFunction) + { + case PASTE_ADD: + bOk = SubTotal::SafePlus( rVal1, nVal2 ); + break; + case PASTE_SUB: + nVal2 = -nVal2; //! geht das immer ohne Fehler? + bOk = SubTotal::SafePlus( rVal1, nVal2 ); + break; + case PASTE_MUL: + bOk = SubTotal::SafeMult( rVal1, nVal2 ); + break; + case PASTE_DIV: + bOk = SubTotal::SafeDiv( rVal1, nVal2 ); + break; + } + return bOk; +} + + +void lcl_AddCode( ScTokenArray& rArr, ScFormulaCell* pCell ) +{ + rArr.AddOpCode(ocOpen); + + ScTokenArray* pCode = pCell->GetCode(); + if (pCode) + { + const formula::FormulaToken* pToken = pCode->First(); + while (pToken) + { + rArr.AddToken( *pToken ); + pToken = pCode->Next(); + } + } + + rArr.AddOpCode(ocClose); +} + + +void ScColumn::MixData( SCROW nRow1, SCROW nRow2, + USHORT nFunction, BOOL bSkipEmpty, + ScColumn& rSrcCol ) +{ + SCSIZE nSrcCount = rSrcCol.nCount; + + SCSIZE nIndex; + Search( nRow1, nIndex ); + +// SCSIZE nSrcIndex = 0; + SCSIZE nSrcIndex; + rSrcCol.Search( nRow1, nSrcIndex ); //! Testen, ob Daten ganz vorne + + SCROW nNextThis = MAXROW+1; + if ( nIndex < nCount ) + nNextThis = pItems[nIndex].nRow; + SCROW nNextSrc = MAXROW+1; + if ( nSrcIndex < nSrcCount ) + nNextSrc = rSrcCol.pItems[nSrcIndex].nRow; + + while ( nNextThis <= nRow2 || nNextSrc <= nRow2 ) + { + SCROW nRow = Min( nNextThis, nNextSrc ); + + ScBaseCell* pSrc = NULL; + ScBaseCell* pDest = NULL; + ScBaseCell* pNew = NULL; + BOOL bDelete = FALSE; + + if ( nSrcIndex < nSrcCount && nNextSrc == nRow ) + pSrc = rSrcCol.pItems[nSrcIndex].pCell; + + if ( nIndex < nCount && nNextThis == nRow ) + pDest = pItems[nIndex].pCell; + + DBG_ASSERT( pSrc || pDest, "Nanu ?" ); + + CellType eSrcType = pSrc ? pSrc->GetCellType() : CELLTYPE_NONE; + CellType eDestType = pDest ? pDest->GetCellType() : CELLTYPE_NONE; + + BOOL bSrcEmpty = ( eSrcType == CELLTYPE_NONE || eSrcType == CELLTYPE_NOTE ); + BOOL bDestEmpty = ( eDestType == CELLTYPE_NONE || eDestType == CELLTYPE_NOTE ); + + if ( bSkipEmpty && bDestEmpty ) // Originalzelle wiederherstellen + { + if ( pSrc ) // war da eine Zelle? + { + pNew = pSrc->CloneWithoutNote( *pDocument ); + } + } + else if ( nFunction ) // wirklich Rechenfunktion angegeben + { + double nVal1; + double nVal2; + if ( eSrcType == CELLTYPE_VALUE ) + nVal1 = ((ScValueCell*)pSrc)->GetValue(); + else + nVal1 = 0.0; + if ( eDestType == CELLTYPE_VALUE ) + nVal2 = ((ScValueCell*)pDest)->GetValue(); + else + nVal2 = 0.0; + + // leere Zellen werden als Werte behandelt + + BOOL bSrcVal = ( bSrcEmpty || eSrcType == CELLTYPE_VALUE ); + BOOL bDestVal = ( bDestEmpty || eDestType == CELLTYPE_VALUE ); + + BOOL bSrcText = ( eSrcType == CELLTYPE_STRING || + eSrcType == CELLTYPE_EDIT ); + BOOL bDestText = ( eDestType == CELLTYPE_STRING || + eDestType == CELLTYPE_EDIT ); + + // sonst bleibt nur Formel... + + if ( bSrcEmpty && bDestEmpty ) + { + // beide leer -> nix + } + else if ( bSrcVal && bDestVal ) + { + // neuen Wert eintragen, oder Fehler bei Ueberlauf + + BOOL bOk = lcl_DoFunction( nVal1, nVal2, nFunction ); + + if (bOk) + pNew = new ScValueCell( nVal1 ); + else + { + ScFormulaCell* pFC = new ScFormulaCell( pDocument, + ScAddress( nCol, nRow, nTab ) ); + pFC->SetErrCode( errNoValue ); + //! oder NOVALUE, dann auch in consoli, + //! sonst in Interpreter::GetCellValue die Abfrage auf errNoValue raus + //! (dann geht Stringzelle+Wertzelle nicht mehr) + pNew = pFC; + } + } + else if ( bSrcText || bDestText ) + { + // mit Texten wird nicht gerechnet - immer "alte" Zelle, also pSrc + + if (pSrc) + pNew = pSrc->CloneWithoutNote( *pDocument ); + else if (pDest) + bDelete = TRUE; + } + else + { + // Kombination aus Wert und mindestens einer Formel -> Formel erzeugen + + ScTokenArray aArr; + + // erste Zelle + if ( eSrcType == CELLTYPE_FORMULA ) + lcl_AddCode( aArr, (ScFormulaCell*)pSrc ); + else + aArr.AddDouble( nVal1 ); + + // Operator + OpCode eOp = ocAdd; + switch ( nFunction ) + { + case PASTE_ADD: eOp = ocAdd; break; + case PASTE_SUB: eOp = ocSub; break; + case PASTE_MUL: eOp = ocMul; break; + case PASTE_DIV: eOp = ocDiv; break; + } + aArr.AddOpCode(eOp); // Funktion + + // zweite Zelle + if ( eDestType == CELLTYPE_FORMULA ) + lcl_AddCode( aArr, (ScFormulaCell*)pDest ); + else + aArr.AddDouble( nVal2 ); + + pNew = new ScFormulaCell( pDocument, ScAddress( nCol, nRow, nTab ), &aArr ); + } + } + + + if ( pNew || bDelete ) // neues Ergebnis ? + { + if (pDest && !pNew) // alte Zelle da ? + { + if ( pDest->GetBroadcaster() ) + pNew = new ScNoteCell; // Broadcaster uebernehmen + else + Delete(nRow); // -> loeschen + } + if (pNew) + Insert(nRow, pNew); // neue einfuegen + + Search( nRow, nIndex ); // alles kann sich verschoben haben + if (pNew) + nNextThis = nRow; // nIndex zeigt jetzt genau auf nRow + else + nNextThis = ( nIndex < nCount ) ? pItems[nIndex].nRow : MAXROW+1; + } + + if ( nNextThis == nRow ) + { + ++nIndex; + nNextThis = ( nIndex < nCount ) ? pItems[nIndex].nRow : MAXROW+1; + } + if ( nNextSrc == nRow ) + { + ++nSrcIndex; + nNextSrc = ( nSrcIndex < nSrcCount ) ? + rSrcCol.pItems[nSrcIndex].nRow : + MAXROW+1; + } + } +} + + +ScAttrIterator* ScColumn::CreateAttrIterator( SCROW nStartRow, SCROW nEndRow ) const +{ + return new ScAttrIterator( pAttrArray, nStartRow, nEndRow ); +} + + +void ScColumn::StartAllListeners() +{ + if (pItems) + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + { + SCROW nRow = pItems[i].nRow; + ((ScFormulaCell*)pCell)->StartListeningTo( pDocument ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener eingefuegt? + } + } +} + + +void ScColumn::StartNeededListeners() +{ + if (pItems) + { + for (SCSIZE i = 0; i < nCount; i++) + { + ScBaseCell* pCell = pItems[i].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFCell = static_cast<ScFormulaCell*>(pCell); + if (pFCell->NeedsListening()) + { + SCROW nRow = pItems[i].nRow; + pFCell->StartListeningTo( pDocument ); + if ( nRow != pItems[i].nRow ) + Search( nRow, i ); // Listener eingefuegt? + } + } + } + } +} + + +void ScColumn::BroadcastInArea( SCROW nRow1, SCROW nRow2 ) +{ + if ( pItems ) + { + SCROW nRow; + SCSIZE nIndex; + Search( nRow1, nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->SetDirty(); + else + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, + ScAddress( nCol, nRow, nTab ), pCell ) ); + nIndex++; + } + } +} + + +void ScColumn::StartListeningInArea( SCROW nRow1, SCROW nRow2 ) +{ + if ( pItems ) + { + SCROW nRow; + SCSIZE nIndex; + Search( nRow1, nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRow2 ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->StartListeningTo( pDocument ); + if ( nRow != pItems[nIndex].nRow ) + Search( nRow, nIndex ); // durch Listening eingefuegt + nIndex++; + } + } +} + + +// TRUE = Zahlformat gesetzt +BOOL ScColumn::SetString( SCROW nRow, SCTAB nTabP, const String& rString, + formula::FormulaGrammar::AddressConvention eConv ) +{ + BOOL bNumFmtSet = FALSE; + if (VALIDROW(nRow)) + { + ScBaseCell* pNewCell = NULL; + BOOL bIsLoading = FALSE; + if (rString.Len() > 0) + { + double nVal; + sal_uInt32 nIndex, nOldIndex = 0; + sal_Unicode cFirstChar; + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); + if ( pDocSh ) + bIsLoading = pDocSh->IsLoading(); + // IsLoading bei ConvertFrom Import + if ( !bIsLoading ) + { + nIndex = nOldIndex = GetNumberFormat( nRow ); + if ( rString.Len() > 1 + && pFormatter->GetType(nIndex) != NUMBERFORMAT_TEXT ) + cFirstChar = rString.GetChar(0); + else + cFirstChar = 0; // Text + } + else + { // waehrend ConvertFrom Import gibt es keine gesetzten Formate + cFirstChar = rString.GetChar(0); + } + + if ( cFirstChar == '=' ) + { + if ( rString.Len() == 1 ) // = Text + pNewCell = new ScStringCell( rString ); + else // =Formel + pNewCell = new ScFormulaCell( pDocument, + ScAddress( nCol, nRow, nTabP ), rString, + formula::FormulaGrammar::mergeToGrammar( formula::FormulaGrammar::GRAM_DEFAULT, + eConv), MM_NONE ); + } + else if ( cFirstChar == '\'') // 'Text + pNewCell = new ScStringCell( rString.Copy(1) ); + else + { + BOOL bIsText = FALSE; + if ( bIsLoading ) + { + if ( pItems && nCount ) + { + String aStr; + SCSIZE i = nCount; + SCSIZE nStop = (i >= 3 ? i - 3 : 0); + // die letzten Zellen vergleichen, ob gleicher String + // und IsNumberFormat eingespart werden kann + do + { + i--; + ScBaseCell* pCell = pItems[i].pCell; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_STRING : + ((ScStringCell*)pCell)->GetString( aStr ); + if ( rString == aStr ) + bIsText = TRUE; + break; + case CELLTYPE_NOTE : // durch =Formel referenziert + break; + default: + if ( i == nCount - 1 ) + i = 0; + // wahrscheinlich ganze Spalte kein String + } + } while ( i && i > nStop && !bIsText ); + } + // nIndex fuer IsNumberFormat vorbelegen + if ( !bIsText ) + nIndex = nOldIndex = pFormatter->GetStandardIndex(); + } + if ( !bIsText && + pFormatter->IsNumberFormat(rString, nIndex, nVal) ) + { // Zahl + pNewCell = new ScValueCell( nVal ); + if ( nIndex != nOldIndex) + { + // #i22345# New behavior: Apply the detected number format only if + // the old one was the default number, date, time or boolean format. + // Exception: If the new format is boolean, always apply it. + + BOOL bOverwrite = FALSE; + const SvNumberformat* pOldFormat = pFormatter->GetEntry( nOldIndex ); + if ( pOldFormat ) + { + short nOldType = pOldFormat->GetType() & ~NUMBERFORMAT_DEFINED; + if ( nOldType == NUMBERFORMAT_NUMBER || nOldType == NUMBERFORMAT_DATE || + nOldType == NUMBERFORMAT_TIME || nOldType == NUMBERFORMAT_LOGICAL ) + { + if ( nOldIndex == pFormatter->GetStandardFormat( + nOldType, pOldFormat->GetLanguage() ) ) + { + bOverwrite = TRUE; // default of these types can be overwritten + } + } + } + if ( !bOverwrite && pFormatter->GetType( nIndex ) == NUMBERFORMAT_LOGICAL ) + { + bOverwrite = TRUE; // overwrite anything if boolean was detected + } + + if ( bOverwrite ) + { + ApplyAttr( nRow, SfxUInt32Item( ATTR_VALUE_FORMAT, + (UINT32) nIndex) ); + bNumFmtSet = TRUE; + } + } + } + else // Text + pNewCell = new ScStringCell( rString ); + } + } + + if ( bIsLoading && (!nCount || nRow > pItems[nCount-1].nRow) ) + { // Search einsparen und ohne Umweg ueber Insert, Listener aufbauen + // und Broadcast kommt eh erst nach dem Laden + if ( pNewCell ) + Append( nRow, pNewCell ); + } + else + { + SCSIZE i; + if (Search(nRow, i)) + { + ScBaseCell* pOldCell = pItems[i].pCell; + ScPostIt* pNote = pOldCell->ReleaseNote(); + SvtBroadcaster* pBC = pOldCell->ReleaseBroadcaster(); + if (pNewCell || pNote || pBC) + { + if (pNewCell) + pNewCell->TakeNote( pNote ); + else + pNewCell = new ScNoteCell( pNote ); + if (pBC) + { + pNewCell->TakeBroadcaster(pBC); + pLastFormulaTreeTop = 0; // Err527 Workaround + } + + if ( pOldCell->GetCellType() == CELLTYPE_FORMULA ) + { + pOldCell->EndListeningTo( pDocument ); + // falls in EndListening NoteCell in gleicher Col zerstoert + if ( i >= nCount || pItems[i].nRow != nRow ) + Search(nRow, i); + } + pOldCell->Delete(); + pItems[i].pCell = pNewCell; // ersetzen + if ( pNewCell->GetCellType() == CELLTYPE_FORMULA ) + { + pNewCell->StartListeningTo( pDocument ); + ((ScFormulaCell*)pNewCell)->SetDirty(); + } + else + pDocument->Broadcast( ScHint( SC_HINT_DATACHANGED, + ScAddress( nCol, nRow, nTabP ), pNewCell ) ); + } + else + { + DeleteAtIndex(i); // loeschen und Broadcast + } + } + else if (pNewCell) + { + Insert(nRow, pNewCell); // neu eintragen und Broadcast + } + } + + // hier keine Formate mehr fuer Formeln setzen! + // (werden bei der Ausgabe abgefragt) + + } + return bNumFmtSet; +} + + +void ScColumn::GetFilterEntries(SCROW nStartRow, SCROW nEndRow, TypedScStrCollection& rStrings) +{ + SvNumberFormatter* pFormatter = pDocument->GetFormatTable(); + String aString; + SCROW nRow = 0; + SCSIZE nIndex; + + Search( nStartRow, nIndex ); + + while ( (nIndex < nCount) ? ((nRow=pItems[nIndex].nRow) <= nEndRow) : FALSE ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + TypedStrData* pData; + ULONG nFormat = GetNumberFormat( nRow ); + + ScCellFormat::GetInputString( pCell, nFormat, aString, *pFormatter ); + + if ( pDocument->HasStringData( nCol, nRow, nTab ) ) + pData = new TypedStrData( aString ); + else + { + double nValue; + + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE: + nValue = ((ScValueCell*)pCell)->GetValue(); + break; + + case CELLTYPE_FORMULA: + nValue = ((ScFormulaCell*)pCell)->GetValue(); + break; + + default: + nValue = 0.0; + } + + pData = new TypedStrData( aString, nValue, SC_STRTYPE_VALUE ); + } +#if 0 // DR + ScPostIt aCellNote( ScPostIt::UNINITIALIZED ); + // Hide visible notes during Filtering. + if(pCell->GetNote(aCellNote) && aCellNote.IsCaptionShown()) + { + ScDetectiveFunc( pDocument, nTab ).HideComment( nCol, nRow ); + aCellNote.SetShown( false ); + pCell->SetNote(aCellNote); + } +#endif + + if ( !rStrings.Insert( pData ) ) + delete pData; // doppelt + + ++nIndex; + } +} + +// +// GetDataEntries - Strings aus zusammenhaengendem Bereich um nRow +// + +// DATENT_MAX - max. Anzahl Eintrage in Liste fuer Auto-Eingabe +// DATENT_SEARCH - max. Anzahl Zellen, die durchsucht werden - neu: nur Strings zaehlen +#define DATENT_MAX 200 +#define DATENT_SEARCH 2000 + + +BOOL ScColumn::GetDataEntries(SCROW nStartRow, TypedScStrCollection& rStrings, BOOL bLimit) +{ + BOOL bFound = FALSE; + SCSIZE nThisIndex; + BOOL bThisUsed = Search( nStartRow, nThisIndex ); + String aString; + USHORT nCells = 0; + + // Die Beschraenkung auf angrenzende Zellen (ohne Luecken) ist nicht mehr gewollt + // (Featurekommission zur 5.1), stattdessen abwechselnd nach oben und unten suchen, + // damit naheliegende Zellen wenigstens zuerst gefunden werden. + //! Abstaende der Zeilennummern vergleichen? (Performance??) + + SCSIZE nUpIndex = nThisIndex; // zeigt hinter die Zelle + SCSIZE nDownIndex = nThisIndex; // zeigt auf die Zelle + if (bThisUsed) + ++nDownIndex; // Startzelle ueberspringen + + while ( nUpIndex || nDownIndex < nCount ) + { + if ( nUpIndex ) // nach oben + { + ScBaseCell* pCell = pItems[nUpIndex-1].pCell; + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT) // nur Strings interessieren + { + if (eType == CELLTYPE_STRING) + ((ScStringCell*)pCell)->GetString(aString); + else + ((ScEditCell*)pCell)->GetString(aString); + + TypedStrData* pData = new TypedStrData(aString); + if ( !rStrings.Insert( pData ) ) + delete pData; // doppelt + else if ( bLimit && rStrings.GetCount() >= DATENT_MAX ) + break; // Maximum erreicht + bFound = TRUE; + + if ( bLimit ) + if (++nCells >= DATENT_SEARCH) + break; // genug gesucht + } + --nUpIndex; + } + + if ( nDownIndex < nCount ) // nach unten + { + ScBaseCell* pCell = pItems[nDownIndex].pCell; + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT) // nur Strings interessieren + { + if (eType == CELLTYPE_STRING) + ((ScStringCell*)pCell)->GetString(aString); + else + ((ScEditCell*)pCell)->GetString(aString); + + TypedStrData* pData = new TypedStrData(aString); + if ( !rStrings.Insert( pData ) ) + delete pData; // doppelt + else if ( bLimit && rStrings.GetCount() >= DATENT_MAX ) + break; // Maximum erreicht + bFound = TRUE; + + if ( bLimit ) + if (++nCells >= DATENT_SEARCH) + break; // genug gesucht + } + ++nDownIndex; + } + } + + return bFound; +} + +#undef DATENT_MAX +#undef DATENT_SEARCH + + +void ScColumn::RemoveProtected( SCROW nStartRow, SCROW nEndRow ) +{ + ScAttrIterator aAttrIter( pAttrArray, nStartRow, nEndRow ); + SCROW nTop; + SCROW nBottom; + SCSIZE nIndex; + const ScPatternAttr* pPattern = aAttrIter.Next( nTop, nBottom ); + while (pPattern) + { + const ScProtectionAttr* pAttr = (const ScProtectionAttr*)&pPattern->GetItem(ATTR_PROTECTION); + if ( pAttr->GetHideCell() ) + DeleteArea( nTop, nBottom, IDF_CONTENTS ); + else if ( pAttr->GetHideFormula() ) + { + Search( nTop, nIndex ); + while ( nIndex<nCount && pItems[nIndex].nRow<=nBottom ) + { + if ( pItems[nIndex].pCell->GetCellType() == CELLTYPE_FORMULA ) + { + ScFormulaCell* pFormula = (ScFormulaCell*)pItems[nIndex].pCell; + if (pFormula->IsValue()) + { + double nVal = pFormula->GetValue(); + pItems[nIndex].pCell = new ScValueCell( nVal ); + } + else + { + String aString; + pFormula->GetString(aString); + pItems[nIndex].pCell = new ScStringCell( aString ); + } + delete pFormula; + } + ++nIndex; + } + } + + pPattern = aAttrIter.Next( nTop, nBottom ); + } +} + + +void ScColumn::SetError( SCROW nRow, const USHORT nError) +{ + if (VALIDROW(nRow)) + { + ScFormulaCell* pCell = new ScFormulaCell + ( pDocument, ScAddress( nCol, nRow, nTab ) ); + pCell->SetErrCode( nError ); + Insert( nRow, pCell ); + } +} + + +void ScColumn::SetValue( SCROW nRow, const double& rVal) +{ + if (VALIDROW(nRow)) + { + ScBaseCell* pCell = new ScValueCell(rVal); + Insert( nRow, pCell ); + } +} + + +void ScColumn::GetString( SCROW nRow, String& rString ) const +{ + SCSIZE nIndex; + Color* pColor; + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if (pCell->GetCellType() != CELLTYPE_NOTE) + { + ULONG nFormat = GetNumberFormat( nRow ); + ScCellFormat::GetString( pCell, nFormat, rString, &pColor, *(pDocument->GetFormatTable()) ); + } + else + rString.Erase(); + } + else + rString.Erase(); +} + + +void ScColumn::GetInputString( SCROW nRow, String& rString ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if (pCell->GetCellType() != CELLTYPE_NOTE) + { + ULONG nFormat = GetNumberFormat( nRow ); + ScCellFormat::GetInputString( pCell, nFormat, rString, *(pDocument->GetFormatTable()) ); + } + else + rString.Erase(); + } + else + rString.Erase(); +} + + +double ScColumn::GetValue( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + return ((ScValueCell*)pCell)->GetValue(); +// break; + case CELLTYPE_FORMULA: + { + if (((ScFormulaCell*)pCell)->IsValue()) + return ((ScFormulaCell*)pCell)->GetValue(); + else + return 0.0; + } +// break; + default: + return 0.0; +// break; + } + } + return 0.0; +} + + +void ScColumn::GetFormula( SCROW nRow, String& rFormula, BOOL ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + ((ScFormulaCell*)pCell)->GetFormula( rFormula ); + else + rFormula.Erase(); + } + else + rFormula.Erase(); +} + + +CellType ScColumn::GetCellType( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + return pItems[nIndex].pCell->GetCellType(); + return CELLTYPE_NONE; +} + + +USHORT ScColumn::GetErrCode( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if (pCell->GetCellType() == CELLTYPE_FORMULA) + return ((ScFormulaCell*)pCell)->GetErrCode(); + } + return 0; +} + + +BOOL ScColumn::HasStringData( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + return (pItems[nIndex].pCell)->HasStringData(); + return FALSE; +} + + +BOOL ScColumn::HasValueData( SCROW nRow ) const +{ + SCSIZE nIndex; + if (Search(nRow, nIndex)) + return (pItems[nIndex].pCell)->HasValueData(); + return FALSE; +} + +BOOL ScColumn::HasStringCells( SCROW nStartRow, SCROW nEndRow ) const +{ + // TRUE, wenn String- oder Editzellen im Bereich + + if ( pItems ) + { + SCSIZE nIndex; + Search( nStartRow, nIndex ); + while ( nIndex < nCount && pItems[nIndex].nRow <= nEndRow ) + { + CellType eType = pItems[nIndex].pCell->GetCellType(); + if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT ) + return TRUE; + ++nIndex; + } + } + return FALSE; +} + + +ScPostIt* ScColumn::GetNote( SCROW nRow ) +{ + SCSIZE nIndex; + return Search( nRow, nIndex ) ? pItems[ nIndex ].pCell->GetNote() : 0; +} + + +void ScColumn::TakeNote( SCROW nRow, ScPostIt* pNote ) +{ + SCSIZE nIndex; + if( Search( nRow, nIndex ) ) + pItems[ nIndex ].pCell->TakeNote( pNote ); + else + Insert( nRow, new ScNoteCell( pNote ) ); +} + + +ScPostIt* ScColumn::ReleaseNote( SCROW nRow ) +{ + ScPostIt* pNote = 0; + SCSIZE nIndex; + if( Search( nRow, nIndex ) ) + { + ScBaseCell* pCell = pItems[ nIndex ].pCell; + pNote = pCell->ReleaseNote(); + if( (pCell->GetCellType() == CELLTYPE_NOTE) && !pCell->GetBroadcaster() ) + DeleteAtIndex( nIndex ); + } + return pNote; +} + + +void ScColumn::DeleteNote( SCROW nRow ) +{ + delete ReleaseNote( nRow ); +} + + +sal_Int32 ScColumn::GetMaxStringLen( SCROW nRowStart, SCROW nRowEnd, CharSet eCharSet ) const +{ + sal_Int32 nStringLen = 0; + if ( pItems ) + { + String aString; + rtl::OString aOString; + bool bIsOctetTextEncoding = rtl_isOctetTextEncoding( eCharSet); + SvNumberFormatter* pNumFmt = pDocument->GetFormatTable(); + SCSIZE nIndex; + SCROW nRow; + Search( nRowStart, nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRowEnd ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + if ( pCell->GetCellType() != CELLTYPE_NOTE ) + { + Color* pColor; + ULONG nFormat = (ULONG) ((SfxUInt32Item*) GetAttr( + nRow, ATTR_VALUE_FORMAT ))->GetValue(); + ScCellFormat::GetString( pCell, nFormat, aString, &pColor, + *pNumFmt ); + sal_Int32 nLen; + if (bIsOctetTextEncoding) + { + rtl::OUString aOUString( aString); + if (!aOUString.convertToString( &aOString, eCharSet, + RTL_UNICODETOTEXT_FLAGS_UNDEFINED_ERROR | + RTL_UNICODETOTEXT_FLAGS_INVALID_ERROR)) + { + // TODO: anything? this is used by the dBase export filter + // that throws an error anyway, but in case of another + // context we might want to indicate a conversion error + // early. + } + nLen = aOString.getLength(); + } + else + nLen = aString.Len() * sizeof(sal_Unicode); + if ( nStringLen < nLen) + nStringLen = nLen; + } + nIndex++; + } + } + return nStringLen; +} + + +xub_StrLen ScColumn::GetMaxNumberStringLen( USHORT& nPrecision, + SCROW nRowStart, SCROW nRowEnd ) const +{ + xub_StrLen nStringLen = 0; + nPrecision = pDocument->GetDocOptions().GetStdPrecision(); + if ( pItems ) + { + String aString; + SvNumberFormatter* pNumFmt = pDocument->GetFormatTable(); + SCSIZE nIndex; + SCROW nRow; + Search( nRowStart, nIndex ); + while ( nIndex < nCount && (nRow = pItems[nIndex].nRow) <= nRowEnd ) + { + ScBaseCell* pCell = pItems[nIndex].pCell; + CellType eType = pCell->GetCellType(); + if ( eType == CELLTYPE_VALUE || (eType == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->IsValue()) ) + { + ULONG nFormat = (ULONG) ((SfxUInt32Item*) GetAttr( + nRow, ATTR_VALUE_FORMAT ))->GetValue(); + ScCellFormat::GetInputString( pCell, nFormat, aString, *pNumFmt ); + xub_StrLen nLen = aString.Len(); + if ( nLen ) + { + if ( nFormat ) + { // more decimals than standard? + USHORT nPrec = pNumFmt->GetFormatPrecision( nFormat ); + if ( nPrec > nPrecision ) + nPrecision = nPrec; + } + if ( nPrecision ) + { // less than nPrecision in string => widen it + // more => shorten it + String aSep = pNumFmt->GetFormatDecimalSep( nFormat ); + xub_StrLen nTmp = aString.Search( aSep ); + if ( nTmp == STRING_NOTFOUND ) + nLen += nPrecision + aSep.Len(); + else + { + nTmp = aString.Len() - (nTmp + aSep.Len()); + if ( nTmp != nPrecision ) + nLen += nPrecision - nTmp; + // nPrecision > nTmp : nLen + Diff + // nPrecision < nTmp : nLen - Diff + } + } + if ( nStringLen < nLen ) + nStringLen = nLen; + } + } + nIndex++; + } + } + return nStringLen; +} + diff --git a/sc/source/core/data/compressedarray.cxx b/sc/source/core/data/compressedarray.cxx new file mode 100644 index 000000000000..71edac0cd0c8 --- /dev/null +++ b/sc/source/core/data/compressedarray.cxx @@ -0,0 +1,909 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: compressedarray.cxx,v $ + * $Revision: 1.10.32.1 $ + * + * 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 "compressedarray.hxx" +#include "address.hxx" + +#include <algorithm> + +template< typename A, typename D > +ScCompressedArray<A,D>::ScCompressedArray( A nMaxAccessP, const D& rValue, + size_t nDeltaP ) + : nCount(1) + , nLimit(1) + , nDelta( nDeltaP > 0 ? nDeltaP : 1) + , pData( new DataEntry[1]) + , nMaxAccess( nMaxAccessP) +{ + pData[0].aValue = rValue; + pData[0].nEnd = nMaxAccess; +} + + +template< typename A, typename D > +ScCompressedArray<A,D>::ScCompressedArray( A nMaxAccessP, const D* pDataArray, + size_t nDataCount ) + : nCount(0) + , nLimit( nDataCount) + , nDelta( nScCompressedArrayDelta) + , pData( new DataEntry[nDataCount]) + , nMaxAccess( nMaxAccessP) +{ + D aValue = pDataArray[0]; + for (size_t j=0; j<nDataCount; ++j) + { + if (!(aValue == pDataArray[j])) + { + pData[nCount].aValue = aValue; + pData[nCount].nEnd = j-1; + ++nCount; + aValue = pDataArray[j]; + } + } + pData[nCount].aValue = aValue; + pData[nCount].nEnd = nMaxAccess; + ++nCount; + Resize( nCount); +} + + +template< typename A, typename D > +ScCompressedArray<A,D>::~ScCompressedArray() +{ + delete[] pData; +} + + +template< typename A, typename D > +void ScCompressedArray<A,D>::Resize( size_t nNewLimit) +{ + if ((nCount <= nNewLimit && nNewLimit < nLimit) || nLimit < nNewLimit) + { + nLimit = nNewLimit; + DataEntry* pNewData = new DataEntry[nLimit]; + memcpy( pNewData, pData, nCount*sizeof(DataEntry)); + delete[] pData; + pData = pNewData; + } +} + + +template< typename A, typename D > +size_t ScCompressedArray<A,D>::Search( A nAccess ) const +{ + if (nAccess == 0) + return 0; + + long nLo = 0; + long nHi = static_cast<long>(nCount) - 1; + long nStart = 0; + long nEnd = 0; + long i = 0; + bool bFound = (nCount == 1); + while (!bFound && nLo <= nHi) + { + i = (nLo + nHi) / 2; + if (i > 0) + nStart = (long) pData[i - 1].nEnd; + else + nStart = -1; + nEnd = (long) pData[i].nEnd; + if (nEnd < (long) nAccess) + nLo = ++i; + else + if (nStart >= (long) nAccess) + nHi = --i; + else + bFound = true; + } + return (bFound ? static_cast<size_t>(i) : (nAccess < 0 ? 0 : nCount-1)); +} + + +template< typename A, typename D > +void ScCompressedArray<A,D>::SetValue( A nStart, A nEnd, const D& rValue ) +{ + if (0 <= nStart && nStart <= nMaxAccess && 0 <= nEnd && nEnd <= nMaxAccess + && nStart <= nEnd) + { + if ((nStart == 0) && (nEnd == nMaxAccess)) + Reset( rValue); + else + { + // Create a temporary copy in case we got a reference passed that + // points to a part of the array to be reallocated. + D aNewVal( rValue); + size_t nNeeded = nCount + 2; + if (nLimit < nNeeded) + { + nLimit += nDelta; + if (nLimit < nNeeded) + nLimit = nNeeded; + DataEntry* pNewData = new DataEntry[nLimit]; + memcpy( pNewData, pData, nCount*sizeof(DataEntry)); + delete[] pData; + pData = pNewData; + } + + size_t ni; // number of leading entries + size_t nInsert; // insert position (nMaxAccess+1 := no insert) + bool bCombined = false; + bool bSplit = false; + if (nStart > 0) + { + // skip leading + ni = Search( nStart); + + nInsert = nMaxAccess+1; + if (!(pData[ni].aValue == aNewVal)) + { + if (ni == 0 || (pData[ni-1].nEnd < nStart - 1)) + { // may be a split or a simple insert or just a shrink, + // row adjustment is done further down + if (pData[ni].nEnd > nEnd) + bSplit = true; + ni++; + nInsert = ni; + } + else if (ni > 0 && pData[ni-1].nEnd == nStart - 1) + nInsert = ni; + } + if (ni > 0 && pData[ni-1].aValue == aNewVal) + { // combine + pData[ni-1].nEnd = nEnd; + nInsert = nMaxAccess+1; + bCombined = true; + } + } + else + { + nInsert = 0; + ni = 0; + } + + size_t nj = ni; // stop position of range to replace + while (nj < nCount && pData[nj].nEnd <= nEnd) + nj++; + if (!bSplit) + { + if (nj < nCount && pData[nj].aValue == aNewVal) + { // combine + if (ni > 0) + { + if (pData[ni-1].aValue == aNewVal) + { // adjacent entries + pData[ni-1].nEnd = pData[nj].nEnd; + nj++; + } + else if (ni == nInsert) + pData[ni-1].nEnd = nStart - 1; // shrink + } + nInsert = nMaxAccess+1; + bCombined = true; + } + else if (ni > 0 && ni == nInsert) + pData[ni-1].nEnd = nStart - 1; // shrink + } + if (ni < nj) + { // remove middle entries + if (!bCombined) + { // replace one entry + pData[ni].nEnd = nEnd; + pData[ni].aValue = aNewVal; + ni++; + nInsert = nMaxAccess+1; + } + if (ni < nj) + { // remove entries + memmove( pData + ni, pData + nj, + (nCount - nj) * sizeof(DataEntry)); + nCount -= nj - ni; + } + } + + if (nInsert < static_cast<size_t>(nMaxAccess+1)) + { // insert or append new entry + if (nInsert <= nCount) + { + if (!bSplit) + memmove( pData + nInsert + 1, pData + nInsert, + (nCount - nInsert) * sizeof(DataEntry)); + else + { + memmove( pData + nInsert + 2, pData + nInsert, + (nCount - nInsert) * sizeof(DataEntry)); + pData[nInsert+1] = pData[nInsert-1]; + nCount++; + } + } + if (nInsert) + pData[nInsert-1].nEnd = nStart - 1; + pData[nInsert].nEnd = nEnd; + pData[nInsert].aValue = aNewVal; + nCount++; + } + } + } +} + + +template< typename A, typename D > +void ScCompressedArray<A,D>::CopyFrom( const ScCompressedArray<A,D>& rArray, A nStart, + A nEnd, long nSourceDy ) +{ + size_t nIndex; + A nRegionEnd; + for (A j=nStart; j<=nEnd; ++j) + { + const D& rValue = (j==nStart ? + rArray.GetValue( j+nSourceDy, nIndex, nRegionEnd) : + rArray.GetNextValue( nIndex, nRegionEnd)); + nRegionEnd -= nSourceDy; + if (nRegionEnd > nEnd) + nRegionEnd = nEnd; + SetValue( j, nRegionEnd, rValue); + j = nRegionEnd; + } +} + + +template< typename A, typename D > +const D& ScCompressedArray<A,D>::Insert( A nStart, size_t nAccessCount ) +{ + size_t nIndex = Search( nStart); + // No real insertion is needed, simply extend the one entry and adapt all + // following. In case nStart points to the start row of an entry, extend + // the previous entry (inserting before nStart). + if (nIndex > 0 && pData[nIndex-1].nEnd+1 == nStart) + --nIndex; + const D& rValue = pData[nIndex].aValue; // the value "copied" + do + { + pData[nIndex].nEnd += nAccessCount; + if (pData[nIndex].nEnd >= nMaxAccess) + { + pData[nIndex].nEnd = nMaxAccess; + nCount = nIndex + 1; // discard trailing entries + } + } while (++nIndex < nCount); + return rValue; +} + + +template< typename A, typename D > +void ScCompressedArray<A,D>::Remove( A nStart, size_t nAccessCount ) +{ + A nEnd = nStart + nAccessCount - 1; + size_t nIndex = Search( nStart); + // equalize/combine/remove all entries in between + if (nEnd > pData[nIndex].nEnd) + SetValue( nStart, nEnd, pData[nIndex].aValue); + // remove an exactly matching entry by shifting up all following by one + if ((nStart == 0 || (nIndex > 0 && nStart == pData[nIndex-1].nEnd+1)) && + pData[nIndex].nEnd == nEnd && nIndex < nCount-1) + { + // In case removing an entry results in two adjacent entries with + // identical data, combine them into one. This is also necessary to + // make the algorithm used in SetValue() work correctly, it relies on + // the fact that consecutive values actually differ. + size_t nRemove; + if (nIndex > 0 && pData[nIndex-1].aValue == pData[nIndex+1].aValue) + { + nRemove = 2; + --nIndex; + } + else + nRemove = 1; + memmove( pData + nIndex, pData + nIndex + nRemove, (nCount - (nIndex + + nRemove)) * sizeof(DataEntry)); + nCount -= nRemove; + } + // adjust end rows, nIndex still being valid + do + { + pData[nIndex].nEnd -= nAccessCount; + } while (++nIndex < nCount); + pData[nCount-1].nEnd = nMaxAccess; +} + + +template< typename A, typename D > +A ScCompressedArray<A,D>::GetLastUnequalAccess( A nStart, const D& rCompare ) +{ + A nEnd = ::std::numeric_limits<A>::max(); + size_t nIndex = nCount-1; + while (1) + { + if (pData[nIndex].aValue != rCompare) + { + nEnd = pData[nIndex].nEnd; + break; // while + } + else + { + if (nIndex > 0) + { + --nIndex; + if (pData[nIndex].nEnd < nStart) + break; // while + } + else + break; // while + } + } + return nEnd; +} + + +// === ScSummableCompressedArray ============================================= + +template< typename A, typename D > +unsigned long ScSummableCompressedArray<A,D>::SumValues( A nStart, A nEnd ) const +{ + size_t nIndex = Search( nStart); + unsigned long nSum = SumValuesContinuation( nStart, nEnd, nIndex); + if (nEnd > this->nMaxAccess) + nSum += this->pData[this->nCount-1].aValue * (nEnd - this->nMaxAccess); + return nSum; +} + + +template< typename A, typename D > +unsigned long ScSummableCompressedArray<A,D>::SumValuesContinuation( + A nStart, A nEnd, size_t& nIndex ) const +{ + unsigned long nSum = 0; + A nS = nStart; + while (nIndex < this->nCount && nS <= nEnd) + { + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + // FIXME: test for overflow in a single region? + unsigned long nNew = (unsigned long) this->pData[nIndex].aValue * (nE - nS + 1); + nSum += nNew; + if (nSum < nNew) + return ::std::numeric_limits<unsigned long>::max(); + nS = nE + 1; + if (nS <= nEnd) + ++nIndex; + } + return nSum; +} + + +template< typename A, typename D > +unsigned long ScSummableCompressedArray<A,D>::SumScaledValuesContinuation( + A nStart, A nEnd, size_t& nIndex, double fScale ) const +{ + unsigned long nSum = 0; + A nS = nStart; + while (nIndex < this->nCount && nS <= nEnd) + { + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + unsigned long nScaledVal = (unsigned long) (this->pData[nIndex].aValue * fScale); + // FIXME: test for overflow in a single region? + unsigned long nNew = nScaledVal * (nE - nS + 1); + nSum += nNew; + if (nSum < nNew) + return ::std::numeric_limits<unsigned long>::max(); + nS = nE + 1; + if (nS <= nEnd) + ++nIndex; + } + return nSum; +} + + +// === ScBitMaskCompressedArray ============================================== + +template< typename A, typename D > +void ScBitMaskCompressedArray<A,D>::AndValue( A nStart, A nEnd, + const D& rValueToAnd ) +{ + if (nStart > nEnd) + return; + + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue & rValueToAnd) != this->pData[nIndex].aValue) + { + A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + SetValue( nS, nE, this->pData[nIndex].aValue & rValueToAnd); + if (nE >= nEnd) + break; // while + nIndex = Search( nE + 1); + } + else if (this->pData[nIndex].nEnd >= nEnd) + break; // while + else + ++nIndex; + } while (nIndex < this->nCount); +} + + +template< typename A, typename D > +void ScBitMaskCompressedArray<A,D>::OrValue( A nStart, A nEnd, + const D& rValueToOr ) +{ + if (nStart > nEnd) + return; + + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue | rValueToOr) != this->pData[nIndex].aValue) + { + A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + SetValue( nS, nE, this->pData[nIndex].aValue | rValueToOr); + if (nE >= nEnd) + break; // while + nIndex = Search( nE + 1); + } + else if (this->pData[nIndex].nEnd >= nEnd) + break; // while + else + ++nIndex; + } while (nIndex < this->nCount); +} + + +template< typename A, typename D > +void ScBitMaskCompressedArray<A,D>::CopyFromAnded( + const ScBitMaskCompressedArray<A,D>& rArray, A nStart, A nEnd, + const D& rValueToAnd, long nSourceDy ) +{ + size_t nIndex; + A nRegionEnd; + for (A j=nStart; j<=nEnd; ++j) + { + const D& rValue = (j==nStart ? + rArray.GetValue( j+nSourceDy, nIndex, nRegionEnd) : + rArray.GetNextValue( nIndex, nRegionEnd)); + nRegionEnd -= nSourceDy; + if (nRegionEnd > nEnd) + nRegionEnd = nEnd; + SetValue( j, nRegionEnd, rValue & rValueToAnd); + j = nRegionEnd; + } +} + + +template< typename A, typename D > +void ScBitMaskCompressedArray<A,D>::CopyFromOred( + const ScBitMaskCompressedArray<A,D>& rArray, A nStart, A nEnd, + const D& rValueToOr, long nSourceDy ) +{ + size_t nIndex; + A nRegionEnd; + for (A j=nStart; j<=nEnd; ++j) + { + const D& rValue = (j==nStart ? + rArray.GetValue( j+nSourceDy, nIndex, nRegionEnd) : + rArray.GetNextValue( nIndex, nRegionEnd)); + nRegionEnd -= nSourceDy; + if (nRegionEnd > nEnd) + nRegionEnd = nEnd; + SetValue( j, nRegionEnd, rValue | rValueToOr); + j = nRegionEnd; + } +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::GetBitStateStart( A nEnd, + const D& rBitMask, const D& rMaskedCompare ) const +{ + A nStart = ::std::numeric_limits<A>::max(); + size_t nIndex = Search( nEnd); + while ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + { + if (nIndex > 0) + { + --nIndex; + nStart = this->pData[nIndex].nEnd + 1; + } + else + { + nStart = 0; + break; // while + } + } + return nStart; +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::GetBitStateEnd( A nStart, + const D& rBitMask, const D& rMaskedCompare ) const +{ + A nEnd = ::std::numeric_limits<A>::max(); + size_t nIndex = Search( nStart); + while (nIndex < this->nCount && (this->pData[nIndex].aValue & rBitMask) == + rMaskedCompare) + { + nEnd = this->pData[nIndex].nEnd; + ++nIndex; + } + return nEnd; +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::GetFirstForCondition( A nStart, A nEnd, + const D& rBitMask, const D& rMaskedCompare ) const +{ + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + { + A nFound = nIndex > 0 ? this->pData[nIndex-1].nEnd + 1 : 0; + return ::std::max( nFound, nStart); + } + if (this->pData[nIndex].nEnd >= nEnd) + break; // while + ++nIndex; + } while (nIndex < this->nCount); + return ::std::numeric_limits<A>::max(); +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::GetLastForCondition( A nStart, A nEnd, + const D& rBitMask, const D& rMaskedCompare ) const +{ + size_t nIndex = Search( nEnd); + while (1) + { + if ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + return ::std::min( this->pData[nIndex].nEnd, nEnd); + + if (nIndex > 0) + { + --nIndex; + if (this->pData[nIndex].nEnd < nStart) + break; // while + } + else + break; // while + } + return ::std::numeric_limits<A>::max(); +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::CountForCondition( A nStart, A nEnd, + const D& rBitMask, const D& rMaskedCompare ) const +{ + A nRet = 0; + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + { + A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + nRet += nE - nS + 1; + } + if (this->pData[nIndex].nEnd >= nEnd) + break; // while + ++nIndex; + } while (nIndex < this->nCount); + return nRet; +} + + +template< typename A, typename D > +size_t ScBitMaskCompressedArray<A,D>::FillArrayForCondition( A nStart, A nEnd, + const D& rBitMask, const D& rMaskedCompare, + A * pArray, size_t nArraySize ) const +{ + size_t nUsed = 0; + size_t nIndex = Search( nStart); + while (nIndex < this->nCount && nUsed < nArraySize) + { + if ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + { + A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + while (nS <= nE && nUsed < nArraySize) + pArray[nUsed++] = nS++; + } + if (this->pData[nIndex].nEnd >= nEnd) + break; // while + ++nIndex; + } + return nUsed; +} + + +template< typename A, typename D > +bool ScBitMaskCompressedArray<A,D>::HasCondition( A nStart, A nEnd, + const D& rBitMask, const D& rMaskedCompare ) const +{ + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue & rBitMask) == rMaskedCompare) + return true; + if (this->pData[nIndex].nEnd >= nEnd) + break; // while + ++nIndex; + } while (nIndex < this->nCount); + return false; +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::CountForAnyBitCondition( A nStart, A nEnd, + const D& rBitMask ) const +{ + A nRet = 0; + size_t nIndex = Search( nStart); + do + { + if ((this->pData[nIndex].aValue & rBitMask) != 0) + { + A nS = ::std::max( (nIndex>0 ? this->pData[nIndex-1].nEnd+1 : 0), nStart); + A nE = ::std::min( this->pData[nIndex].nEnd, nEnd); + nRet += nE - nS + 1; + } + if (this->pData[nIndex].nEnd >= nEnd) + break; // while + ++nIndex; + } while (nIndex < this->nCount); + return nRet; +} + + +template< typename A, typename D > +A ScBitMaskCompressedArray<A,D>::GetLastAnyBitAccess( A nStart, + const D& rBitMask ) const +{ + A nEnd = ::std::numeric_limits<A>::max(); + size_t nIndex = this->nCount-1; + while (1) + { + if ((this->pData[nIndex].aValue & rBitMask) != 0) + { + nEnd = this->pData[nIndex].nEnd; + break; // while + } + else + { + if (nIndex > 0) + { + --nIndex; + if (this->pData[nIndex].nEnd < nStart) + break; // while + } + else + break; // while + } + } + return nEnd; +} + + +template< typename A, typename D > +template< typename S > +unsigned long ScBitMaskCompressedArray<A,D>::SumCoupledArrayForCondition( + A nStart, A nEnd, const D& rBitMask, const D& rMaskedCompare, + const ScSummableCompressedArray<A,S>& rArray ) const +{ + unsigned long nSum = 0; + A nS = nStart; + size_t nIndex1 = Search( nStart); + size_t nIndex2 = rArray.Search( nStart); + do + { + if ((this->pData[nIndex1].aValue & rBitMask) == rMaskedCompare) + { + while (nIndex2 < rArray.GetEntryCount() && + rArray.GetDataEntry(nIndex2).nEnd < nS) + ++nIndex2; + unsigned long nNew = rArray.SumValuesContinuation( nS, + ::std::min( this->pData[nIndex1].nEnd, nEnd), nIndex2); + nSum += nNew; + if (nSum < nNew) + return ::std::numeric_limits<unsigned long>::max(); + } + nS = this->pData[nIndex1].nEnd + 1; + ++nIndex1; + } while (nIndex1 < this->nCount && nS <= nEnd); + if (nEnd > this->nMaxAccess && + (this->pData[this->GetEntryCount()-1].aValue & rBitMask) == rMaskedCompare) + nSum += rArray.GetDataEntry(rArray.GetEntryCount()-1).aValue * (nEnd - + this->nMaxAccess); + return nSum; +} + + +template< typename A, typename D > +template< typename S > +unsigned long ScBitMaskCompressedArray<A,D>::SumScaledCoupledArrayForCondition( + A nStart, A nEnd, const D& rBitMask, const D& rMaskedCompare, + const ScSummableCompressedArray<A,S>& rArray, double fScale ) const +{ + unsigned long nSum = 0; + A nS = nStart; + size_t nIndex1 = Search( nStart); + size_t nIndex2 = rArray.Search( nStart); + do + { + if ((this->pData[nIndex1].aValue & rBitMask) == rMaskedCompare) + { + while (nIndex2 < rArray.GetEntryCount() && + rArray.GetDataEntry(nIndex2).nEnd < nS) + ++nIndex2; + unsigned long nNew = rArray.SumScaledValuesContinuation( nS, + ::std::min( this->pData[nIndex1].nEnd, nEnd), nIndex2, fScale); + nSum += nNew; + if (nSum < nNew) + return ::std::numeric_limits<unsigned long>::max(); + } + nS = this->pData[nIndex1].nEnd + 1; + ++nIndex1; + } while (nIndex1 < this->nCount && nS <= nEnd); + if (nEnd > this->nMaxAccess && + (this->pData[this->GetEntryCount()-1].aValue & rBitMask) == rMaskedCompare) + nSum += (unsigned long) + (rArray.GetDataEntry(rArray.GetEntryCount()-1).aValue * fScale) * + (nEnd - this->nMaxAccess); + return nSum; +} + + +// === ScCompressedArrayIterator ============================================= + +template< typename A, typename D > +template< typename X > +void ScCompressedArrayIterator<A,D>::Follow( + const ScCompressedArrayIterator<A,X>& rIter ) +{ + nCurrent = rIter.GetPos(); + if (GetRangeStart() <= nCurrent && nCurrent <= GetRangeEnd()) + ; // nothing + else if (nCurrent > GetRangeEnd()) + { + A nPos = nCurrent; // nCurrent gets changed in NextRange() + bool bAdv; + do + { + bAdv = NextRange(); + } while (bAdv && GetRangeEnd() < nPos); + nCurrent = nPos; + } + else + nIndex = rArray.Search( nCurrent); +} + + +// === ScCoupledCompressedArrayIterator ====================================== + +template< typename A, typename D, typename S > +ScCoupledCompressedArrayIterator<A,D,S>::ScCoupledCompressedArrayIterator( + const ScBitMaskCompressedArray<A,D> & rArray1, A nStart, A nEnd, + const D& rBitMaskP, const D& rMaskedCompareP, + const ScCompressedArray<A,S> & rArray2 ) + : aIter1( rArray1, nStart, nEnd ) + , aIter2( rArray2, nStart, nEnd ) + , rBitMask( rBitMaskP ) + , rMaskedCompare( rMaskedCompareP ) +{ + InitLimits(); +} + + +template< typename A, typename D, typename S > +void ScCoupledCompressedArrayIterator<A,D,S>::InitLimits() +{ + bool bFound = true; + bool bMoved = false; + while (bFound && ((*aIter1 & rBitMask) != rMaskedCompare)) + { + bFound = aIter1.NextRange(); + bMoved = true; + } + if (bMoved && bFound) + aIter2.Follow( aIter1); +} + + +template< typename A, typename D, typename S > +void ScCoupledCompressedArrayIterator<A,D,S>::NewLimits( A nStart, A nEnd ) +{ + aIter1.NewLimits( nStart, nEnd); + aIter2.NewLimits( nStart, nEnd); + InitLimits(); +} + + +template< typename A, typename D, typename S > +bool ScCoupledCompressedArrayIterator<A,D,S>::NextRange() +{ + bool bAdv; + if (aIter1.GetRangeEnd() <= aIter2.GetRangeEnd()) + { + // Advance bit mask array until condition is met, coupled array + // follows. + do + { + bAdv = aIter1.NextRange(); + } while (bAdv && ((*aIter1 & rBitMask) != rMaskedCompare)); + if (bAdv) + aIter2.Follow( aIter1); + } + else + { + // Make coupled array catch up with bit mask array. + do + { + bAdv = aIter2.NextRange(); + } while (bAdv && aIter2.GetRangeEnd() < aIter1.GetRangeStart()); + if (bAdv) + aIter1.Follow( aIter2); // synchronize aIter1.nCurrent + } + return operator bool(); +} + + +template< typename A, typename D, typename S > +void ScCoupledCompressedArrayIterator<A,D,S>::Resync( A nPos ) +{ + aIter1.Resync( nPos); + aIter2.Resync( nPos); + InitLimits(); +} + + +// === Force instantiation of specializations ================================ + +template class ScCompressedArray< SCROW, USHORT>; // heights, base class +template class ScSummableCompressedArray< SCROW, USHORT>; // heights +template class ScCompressedArray< SCROW, BYTE>; // flags, base class +template class ScBitMaskCompressedArray< SCROW, BYTE>; // flags +template unsigned long ScBitMaskCompressedArray< SCROW, + BYTE>::SumCoupledArrayForCondition( SCROW, SCROW, const BYTE&, const BYTE&, + const ScSummableCompressedArray< SCROW, USHORT>&) const; +template unsigned long ScBitMaskCompressedArray< SCROW, + BYTE>::SumScaledCoupledArrayForCondition( SCROW, SCROW, const BYTE&, + const BYTE&, const ScSummableCompressedArray< SCROW, USHORT>&, + double) const; +template void ScCompressedArrayIterator< SCROW, USHORT>::Follow( + const ScCompressedArrayIterator< SCROW, BYTE>&); +template class ScCoupledCompressedArrayIterator< SCROW, BYTE, USHORT>; + +// === EOF =================================================================== diff --git a/sc/source/core/data/conditio.cxx b/sc/source/core/data/conditio.cxx new file mode 100644 index 000000000000..45ead48d0f35 --- /dev/null +++ b/sc/source/core/data/conditio.cxx @@ -0,0 +1,1606 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: conditio.cxx,v $ + * $Revision: 1.25.30.5 $ + * + * 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 "scitems.hxx" +#include <sfx2/objsh.hxx> +#include <svtools/itemset.hxx> +#include <svtools/zforlist.hxx> +#include <rtl/math.hxx> +#include <unotools/collatorwrapper.hxx> + +#include "conditio.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "hints.hxx" +#include "compiler.hxx" +#include "rechead.hxx" +#include "rangelst.hxx" +#include "stlpool.hxx" +#include "rangenam.hxx" + +using namespace formula; +//------------------------------------------------------------------------ + +SV_IMPL_OP_PTRARR_SORT( ScConditionalFormats_Impl, ScConditionalFormatPtr ); + +//------------------------------------------------------------------------ + +BOOL lcl_HasRelRef( ScDocument* pDoc, ScTokenArray* pFormula, USHORT nRecursion = 0 ) +{ + if (pFormula) + { + pFormula->Reset(); + FormulaToken* t; + for( t = pFormula->Next(); t; t = pFormula->Next() ) + { + switch( t->GetType() ) + { + case svDoubleRef: + { + ScSingleRefData& rRef2 = static_cast<ScToken*>(t)->GetDoubleRef().Ref2; + if ( rRef2.IsColRel() || rRef2.IsRowRel() || rRef2.IsTabRel() ) + return TRUE; + } + // fall through + + case svSingleRef: + { + ScSingleRefData& rRef1 = static_cast<ScToken*>(t)->GetSingleRef(); + if ( rRef1.IsColRel() || rRef1.IsRowRel() || rRef1.IsTabRel() ) + return TRUE; + } + break; + + case svIndex: + { + if( t->GetOpCode() == ocName ) // DB areas always absolute + if( ScRangeData* pRangeData = pDoc->GetRangeName()->FindIndex( t->GetIndex() ) ) + if( (nRecursion < 42) && lcl_HasRelRef( pDoc, pRangeData->GetCode(), nRecursion + 1 ) ) + return TRUE; + } + break; + + // #i34474# function result dependent on cell position + case svByte: + { + switch( t->GetOpCode() ) + { + case ocRow: // ROW() returns own row index + case ocColumn: // COLUMN() returns own column index + case ocTable: // SHEET() returns own sheet index + case ocCell: // CELL() may return own cell address + return TRUE; +// break; + default: + { + // added to avoid warnings + } + } + } + break; + + default: + { + // added to avoid warnings + } + } + } + } + return FALSE; +} + +ScConditionEntry::ScConditionEntry( const ScConditionEntry& r ) : + eOp(r.eOp), + nOptions(r.nOptions), + nVal1(r.nVal1), + nVal2(r.nVal2), + aStrVal1(r.aStrVal1), + aStrVal2(r.aStrVal2), + aStrNmsp1(r.aStrNmsp1), + aStrNmsp2(r.aStrNmsp2), + eTempGrammar1(r.eTempGrammar1), + eTempGrammar2(r.eTempGrammar2), + bIsStr1(r.bIsStr1), + bIsStr2(r.bIsStr2), + pFormula1(NULL), + pFormula2(NULL), + aSrcPos(r.aSrcPos), + aSrcString(r.aSrcString), + pFCell1(NULL), + pFCell2(NULL), + pDoc(r.pDoc), + bRelRef1(r.bRelRef1), + bRelRef2(r.bRelRef2), + bFirstRun(TRUE) +{ + // ScTokenArray copy ctor erzeugt flache Kopie + + if (r.pFormula1) + pFormula1 = new ScTokenArray( *r.pFormula1 ); + if (r.pFormula2) + pFormula2 = new ScTokenArray( *r.pFormula2 ); + + // Formelzellen werden erst bei IsValid angelegt +} + +ScConditionEntry::ScConditionEntry( ScDocument* pDocument, const ScConditionEntry& r ) : + eOp(r.eOp), + nOptions(r.nOptions), + nVal1(r.nVal1), + nVal2(r.nVal2), + aStrVal1(r.aStrVal1), + aStrVal2(r.aStrVal2), + aStrNmsp1(r.aStrNmsp1), + aStrNmsp2(r.aStrNmsp2), + eTempGrammar1(r.eTempGrammar1), + eTempGrammar2(r.eTempGrammar2), + bIsStr1(r.bIsStr1), + bIsStr2(r.bIsStr2), + pFormula1(NULL), + pFormula2(NULL), + aSrcPos(r.aSrcPos), + aSrcString(r.aSrcString), + pFCell1(NULL), + pFCell2(NULL), + pDoc(pDocument), + bRelRef1(r.bRelRef1), + bRelRef2(r.bRelRef2), + bFirstRun(TRUE) +{ + // echte Kopie der Formeln (fuer Ref-Undo) + + if (r.pFormula1) + pFormula1 = r.pFormula1->Clone(); + if (r.pFormula2) + pFormula2 = r.pFormula2->Clone(); + + // Formelzellen werden erst bei IsValid angelegt + //! im Clipboard nicht - dann vorher interpretieren !!! +} + +ScConditionEntry::ScConditionEntry( ScConditionMode eOper, + const String& rExpr1, const String& rExpr2, ScDocument* pDocument, const ScAddress& rPos, + const String& rExprNmsp1, const String& rExprNmsp2, + FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) : + eOp(eOper), + nOptions(0), // spaeter... + nVal1(0.0), + nVal2(0.0), + aStrNmsp1(rExprNmsp1), + aStrNmsp2(rExprNmsp2), + eTempGrammar1(eGrammar1), + eTempGrammar2(eGrammar2), + bIsStr1(FALSE), + bIsStr2(FALSE), + pFormula1(NULL), + pFormula2(NULL), + aSrcPos(rPos), + pFCell1(NULL), + pFCell2(NULL), + pDoc(pDocument), + bRelRef1(FALSE), + bRelRef2(FALSE), + bFirstRun(TRUE) +{ + Compile( rExpr1, rExpr2, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2, FALSE ); + + // Formelzellen werden erst bei IsValid angelegt +} + +ScConditionEntry::ScConditionEntry( ScConditionMode eOper, + const ScTokenArray* pArr1, const ScTokenArray* pArr2, + ScDocument* pDocument, const ScAddress& rPos ) : + eOp(eOper), + nOptions(0), // spaeter... + nVal1(0.0), + nVal2(0.0), + eTempGrammar1(FormulaGrammar::GRAM_DEFAULT), + eTempGrammar2(FormulaGrammar::GRAM_DEFAULT), + bIsStr1(FALSE), + bIsStr2(FALSE), + pFormula1(NULL), + pFormula2(NULL), + aSrcPos(rPos), + pFCell1(NULL), + pFCell2(NULL), + pDoc(pDocument), + bRelRef1(FALSE), + bRelRef2(FALSE), + bFirstRun(TRUE) +{ + if ( pArr1 ) + { + pFormula1 = new ScTokenArray( *pArr1 ); + if ( pFormula1->GetLen() == 1 ) + { + // einzelne (konstante Zahl) ? + FormulaToken* pToken = pFormula1->First(); + if ( pToken->GetOpCode() == ocPush ) + { + if ( pToken->GetType() == svDouble ) + { + nVal1 = pToken->GetDouble(); + DELETEZ(pFormula1); // nicht als Formel merken + } + else if ( pToken->GetType() == svString ) + { + bIsStr1 = TRUE; + aStrVal1 = pToken->GetString(); + DELETEZ(pFormula1); // nicht als Formel merken + } + } + } + bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); + } + if ( pArr2 ) + { + pFormula2 = new ScTokenArray( *pArr2 ); + if ( pFormula2->GetLen() == 1 ) + { + // einzelne (konstante Zahl) ? + FormulaToken* pToken = pFormula2->First(); + if ( pToken->GetOpCode() == ocPush ) + { + if ( pToken->GetType() == svDouble ) + { + nVal2 = pToken->GetDouble(); + DELETEZ(pFormula2); // nicht als Formel merken + } + else if ( pToken->GetType() == svString ) + { + bIsStr2 = TRUE; + aStrVal2 = pToken->GetString(); + DELETEZ(pFormula2); // nicht als Formel merken + } + } + } + bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); + } + + // formula cells are created at IsValid +} + +ScConditionEntry::~ScConditionEntry() +{ + delete pFCell1; + delete pFCell2; + + delete pFormula1; + delete pFormula2; +} + +void ScConditionEntry::Compile( const String& rExpr1, const String& rExpr2, + const String& rExprNmsp1, const String& rExprNmsp2, + FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2, BOOL bTextToReal ) +{ + if ( rExpr1.Len() || rExpr2.Len() ) + { + ScCompiler aComp( pDoc, aSrcPos ); + + if ( rExpr1.Len() ) + { + aComp.SetGrammar( eGrammar1 ); + if ( pDoc->IsImportingXML() && !bTextToReal ) + { + // temporary formula string as string tokens + //! merge with lcl_ScDocFunc_CreateTokenArrayXML + pFormula1 = new ScTokenArray; + pFormula1->AddString( rExpr1 ); + // bRelRef1 is set when the formula is compiled again (CompileXML) + } + else + { + pFormula1 = aComp.CompileString( rExpr1, rExprNmsp1 ); + if ( pFormula1->GetLen() == 1 ) + { + // einzelne (konstante Zahl) ? + FormulaToken* pToken = pFormula1->First(); + if ( pToken->GetOpCode() == ocPush ) + { + if ( pToken->GetType() == svDouble ) + { + nVal1 = pToken->GetDouble(); + DELETEZ(pFormula1); // nicht als Formel merken + } + else if ( pToken->GetType() == svString ) + { + bIsStr1 = TRUE; + aStrVal1 = pToken->GetString(); + DELETEZ(pFormula1); // nicht als Formel merken + } + } + } + bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); + } + } + + if ( rExpr2.Len() ) + { + aComp.SetGrammar( eGrammar2 ); + if ( pDoc->IsImportingXML() && !bTextToReal ) + { + // temporary formula string as string tokens + //! merge with lcl_ScDocFunc_CreateTokenArrayXML + pFormula2 = new ScTokenArray; + pFormula2->AddString( rExpr2 ); + // bRelRef2 is set when the formula is compiled again (CompileXML) + } + else + { + pFormula2 = aComp.CompileString( rExpr2, rExprNmsp2 ); + if ( pFormula2->GetLen() == 1 ) + { + // einzelne (konstante Zahl) ? + FormulaToken* pToken = pFormula2->First(); + if ( pToken->GetOpCode() == ocPush ) + { + if ( pToken->GetType() == svDouble ) + { + nVal2 = pToken->GetDouble(); + DELETEZ(pFormula2); // nicht als Formel merken + } + else if ( pToken->GetType() == svString ) + { + bIsStr2 = TRUE; + aStrVal2 = pToken->GetString(); + DELETEZ(pFormula2); // nicht als Formel merken + } + } + } + bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); + } + } + } +} + +void ScConditionEntry::MakeCells( const ScAddress& rPos ) // Formelzellen anlegen +{ + if ( !pDoc->IsClipOrUndo() ) // nie im Clipboard rechnen! + { + if ( pFormula1 && !pFCell1 && !bRelRef1 ) + { + pFCell1 = new ScFormulaCell( pDoc, rPos, pFormula1 ); + pFCell1->StartListeningTo( pDoc ); + } + + if ( pFormula2 && !pFCell2 && !bRelRef2 ) + { + pFCell2 = new ScFormulaCell( pDoc, rPos, pFormula2 ); + pFCell2->StartListeningTo( pDoc ); + } + } +} + +void ScConditionEntry::SetIgnoreBlank(BOOL bSet) +{ + // Das Bit SC_COND_NOBLANKS wird gesetzt, wenn Blanks nicht ignoriert werden + // (nur bei Gueltigkeit) + + if (bSet) + nOptions &= ~SC_COND_NOBLANKS; + else + nOptions |= SC_COND_NOBLANKS; +} + +void ScConditionEntry::CompileAll() +{ + // Formelzellen loeschen, dann wird beim naechsten IsValid neu kompiliert + + DELETEZ(pFCell1); + DELETEZ(pFCell2); +} + +void ScConditionEntry::CompileXML() +{ + // #b4974740# First parse the formula source position if it was stored as text + + if ( aSrcString.Len() ) + { + ScAddress aNew; + /* XML is always in OOo:A1 format, although R1C1 would be more amenable + * to compression */ + if ( aNew.Parse( aSrcString, pDoc ) & SCA_VALID ) + aSrcPos = aNew; + // if the position is invalid, there isn't much we can do at this time + aSrcString.Erase(); + } + + // Convert the text tokens that were created during XML import into real tokens. + + Compile( GetExpression(aSrcPos, 0, 0, eTempGrammar1), + GetExpression(aSrcPos, 1, 0, eTempGrammar2), + aStrNmsp1, aStrNmsp2, eTempGrammar1, eTempGrammar2, TRUE ); +} + +void ScConditionEntry::SetSrcString( const String& rNew ) +{ + // aSrcString is only evaluated in CompileXML + DBG_ASSERT( pDoc->IsImportingXML(), "SetSrcString is only valid for XML import" ); + + aSrcString = rNew; +} + +void ScConditionEntry::SetFormula1( const ScTokenArray& rArray ) +{ + DELETEZ( pFormula1 ); + if( rArray.GetLen() > 0 ) + { + pFormula1 = new ScTokenArray( rArray ); + bRelRef1 = lcl_HasRelRef( pDoc, pFormula1 ); + } +} + +void ScConditionEntry::SetFormula2( const ScTokenArray& rArray ) +{ + DELETEZ( pFormula2 ); + if( rArray.GetLen() > 0 ) + { + pFormula2 = new ScTokenArray( rArray ); + bRelRef2 = lcl_HasRelRef( pDoc, pFormula2 ); + } +} + +void lcl_CondUpdateInsertTab( ScTokenArray& rCode, SCTAB nInsTab, SCTAB nPosTab, BOOL& rChanged ) +{ + // Insert table: only update absolute table references. + // (Similar to ScCompiler::UpdateInsertTab with bIsName=TRUE, result is the same as for named ranges) + // For deleting, ScCompiler::UpdateDeleteTab is used because of the handling of invalid references. + + rCode.Reset(); + ScToken* p = static_cast<ScToken*>(rCode.GetNextReference()); + while( p ) + { + ScSingleRefData& rRef1 = p->GetSingleRef(); + if ( !rRef1.IsTabRel() && nInsTab <= rRef1.nTab ) + { + rRef1.nTab += 1; + rRef1.nRelTab = rRef1.nTab - nPosTab; + rChanged = TRUE; + } + if( p->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = p->GetDoubleRef().Ref2; + if ( !rRef2.IsTabRel() && nInsTab <= rRef2.nTab ) + { + rRef2.nTab += 1; + rRef2.nRelTab = rRef2.nTab - nPosTab; + rChanged = TRUE; + } + } + p = static_cast<ScToken*>(rCode.GetNextReference()); + } +} + +void ScConditionEntry::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + BOOL bInsertTab = ( eUpdateRefMode == URM_INSDEL && nDz == 1 ); + BOOL bDeleteTab = ( eUpdateRefMode == URM_INSDEL && nDz == -1 ); + + BOOL bChanged1 = FALSE; + BOOL bChanged2 = FALSE; + + if (pFormula1) + { + if ( bInsertTab ) + lcl_CondUpdateInsertTab( *pFormula1, rRange.aStart.Tab(), aSrcPos.Tab(), bChanged1 ); + else + { + ScCompiler aComp( pDoc, aSrcPos, *pFormula1 ); + aComp.SetGrammar(pDoc->GetGrammar()); + if ( bDeleteTab ) + aComp.UpdateDeleteTab( rRange.aStart.Tab(), FALSE, TRUE, bChanged1 ); + else + aComp.UpdateNameReference( eUpdateRefMode, rRange, nDx, nDy, nDz, bChanged1 ); + } + + if (bChanged1) + DELETEZ(pFCell1); // is created again in IsValid + } + if (pFormula2) + { + if ( bInsertTab ) + lcl_CondUpdateInsertTab( *pFormula2, rRange.aStart.Tab(), aSrcPos.Tab(), bChanged2 ); + else + { + ScCompiler aComp( pDoc, aSrcPos, *pFormula2); + aComp.SetGrammar(pDoc->GetGrammar()); + if ( bDeleteTab ) + aComp.UpdateDeleteTab( rRange.aStart.Tab(), FALSE, TRUE, bChanged2 ); + else + aComp.UpdateNameReference( eUpdateRefMode, rRange, nDx, nDy, nDz, bChanged2 ); + } + + if (bChanged2) + DELETEZ(pFCell2); // is created again in IsValid + } +} + +void ScConditionEntry::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + if (pFormula1) + { + ScCompiler aComp( pDoc, aSrcPos, *pFormula1); + aComp.SetGrammar(pDoc->GetGrammar()); + aComp.UpdateMoveTab(nOldPos, nNewPos, TRUE ); + DELETEZ(pFCell1); + } + if (pFormula2) + { + ScCompiler aComp( pDoc, aSrcPos, *pFormula2); + aComp.SetGrammar(pDoc->GetGrammar()); + aComp.UpdateMoveTab(nOldPos, nNewPos, TRUE ); + DELETEZ(pFCell2); + } +} + +//! als Vergleichsoperator ans TokenArray ??? + +BOOL lcl_IsEqual( const ScTokenArray* pArr1, const ScTokenArray* pArr2 ) +{ + // verglichen wird nur das nicht-UPN Array + + if ( pArr1 && pArr2 ) + { + USHORT nLen = pArr1->GetLen(); + if ( pArr2->GetLen() != nLen ) + return FALSE; + + FormulaToken** ppToken1 = pArr1->GetArray(); + FormulaToken** ppToken2 = pArr2->GetArray(); + for (USHORT i=0; i<nLen; i++) + { + if ( ppToken1[i] != ppToken2[i] && + !(*ppToken1[i] == *ppToken2[i]) ) + return FALSE; // Unterschied + } + return TRUE; // alle Eintraege gleich + } + else + return !pArr1 && !pArr2; // beide 0 -> gleich +} + +int ScConditionEntry::operator== ( const ScConditionEntry& r ) const +{ + BOOL bEq = (eOp == r.eOp && nOptions == r.nOptions && + lcl_IsEqual( pFormula1, r.pFormula1 ) && + lcl_IsEqual( pFormula2, r.pFormula2 )); + if (bEq) + { + // for formulas, the reference positions must be compared, too + // (including aSrcString, for inserting the entries during XML import) + if ( ( pFormula1 || pFormula2 ) && ( aSrcPos != r.aSrcPos || aSrcString != r.aSrcString ) ) + bEq = FALSE; + + // wenn keine Formeln, Werte vergleichen + if ( !pFormula1 && ( nVal1 != r.nVal1 || aStrVal1 != r.aStrVal1 || bIsStr1 != r.bIsStr1 ) ) + bEq = FALSE; + if ( !pFormula2 && ( nVal2 != r.nVal2 || aStrVal2 != r.aStrVal2 || bIsStr2 != r.bIsStr2 ) ) + bEq = FALSE; + } + + return bEq; +} + +void ScConditionEntry::Interpret( const ScAddress& rPos ) +{ + // Formelzellen anlegen + // dabei koennen neue Broadcaster (Note-Zellen) ins Dokument eingefuegt werden !!!! + + if ( ( pFormula1 && !pFCell1 ) || ( pFormula2 && !pFCell2 ) ) + MakeCells( rPos ); + + // Formeln auswerten + + BOOL bDirty = FALSE; //! 1 und 2 getrennt ??? + + ScFormulaCell* pTemp1 = NULL; + ScFormulaCell* pEff1 = pFCell1; + if ( bRelRef1 ) + { + pTemp1 = new ScFormulaCell( pDoc, rPos, pFormula1 ); // ohne Listening + pEff1 = pTemp1; + } + if ( pEff1 ) + { + if (!pEff1->IsRunning()) // keine 522 erzeugen + { + //! Changed statt Dirty abfragen !!! + if (pEff1->GetDirty() && !bRelRef1) + bDirty = TRUE; + if (pEff1->IsValue()) + { + bIsStr1 = FALSE; + nVal1 = pEff1->GetValue(); + aStrVal1.Erase(); + } + else + { + bIsStr1 = TRUE; + pEff1->GetString( aStrVal1 ); + nVal1 = 0.0; + } + } + } + delete pTemp1; + + ScFormulaCell* pTemp2 = NULL; + ScFormulaCell* pEff2 = pFCell2; //@ 1!=2 + if ( bRelRef2 ) + { + pTemp2 = new ScFormulaCell( pDoc, rPos, pFormula2 ); // ohne Listening + pEff2 = pTemp2; + } + if ( pEff2 ) + { + if (!pEff2->IsRunning()) // keine 522 erzeugen + { + if (pEff2->GetDirty() && !bRelRef2) + bDirty = TRUE; + if (pEff2->IsValue()) + { + bIsStr2 = FALSE; + nVal2 = pEff2->GetValue(); + aStrVal2.Erase(); + } + else + { + bIsStr2 = TRUE; + pEff2->GetString( aStrVal2 ); + nVal2 = 0.0; + } + } + } + delete pTemp2; + + // wenn IsRunning, bleiben die letzten Werte erhalten + + if (bDirty && !bFirstRun) + { + // bei bedingten Formaten neu painten + + DataChanged( NULL ); // alles + } + + bFirstRun = FALSE; +} + +BOOL ScConditionEntry::IsValid( double nArg ) const +{ + // Interpret muss schon gerufen sein + + if ( bIsStr1 ) + { + // wenn auf String getestet wird, bei Zahlen immer FALSE, ausser bei "ungleich" + + return ( eOp == SC_COND_NOTEQUAL ); + } + + if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) + if ( bIsStr2 ) + return FALSE; + + double nComp1 = nVal1; // Kopie, damit vertauscht werden kann + double nComp2 = nVal2; + + if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) + if ( nComp1 > nComp2 ) + { + // richtige Reihenfolge fuer Wertebereich + double nTemp = nComp1; nComp1 = nComp2; nComp2 = nTemp; + } + + // Alle Grenzfaelle muessen per ::rtl::math::approxEqual getestet werden! + + BOOL bValid = FALSE; + switch (eOp) + { + case SC_COND_NONE: + break; // immer FALSE; + case SC_COND_EQUAL: + bValid = ::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_NOTEQUAL: + bValid = !::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_GREATER: + bValid = ( nArg > nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_EQGREATER: + bValid = ( nArg >= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_LESS: + bValid = ( nArg < nComp1 ) && !::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_EQLESS: + bValid = ( nArg <= nComp1 ) || ::rtl::math::approxEqual( nArg, nComp1 ); + break; + case SC_COND_BETWEEN: + bValid = ( nArg >= nComp1 && nArg <= nComp2 ) || + ::rtl::math::approxEqual( nArg, nComp1 ) || ::rtl::math::approxEqual( nArg, nComp2 ); + break; + case SC_COND_NOTBETWEEN: + bValid = ( nArg < nComp1 || nArg > nComp2 ) && + !::rtl::math::approxEqual( nArg, nComp1 ) && !::rtl::math::approxEqual( nArg, nComp2 ); + break; + case SC_COND_DIRECT: + bValid = !::rtl::math::approxEqual( nComp1, 0.0 ); + break; + default: + DBG_ERROR("unbekannte Operation bei ScConditionEntry"); + break; + } + return bValid; +} + +BOOL ScConditionEntry::IsValidStr( const String& rArg ) const +{ + // Interpret muss schon gerufen sein + + if ( eOp == SC_COND_DIRECT ) // Formel ist unabhaengig vom Inhalt + return !::rtl::math::approxEqual( nVal1, 0.0 ); + + // Wenn Bedingung Zahl enthaelt, immer FALSE, ausser bei "ungleich" + + if ( !bIsStr1 ) + return ( eOp == SC_COND_NOTEQUAL ); + if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) + if ( !bIsStr2 ) + return FALSE; + + String aUpVal1( aStrVal1 ); //! als Member? (dann auch in Interpret setzen) + String aUpVal2( aStrVal2 ); + + if ( eOp == SC_COND_BETWEEN || eOp == SC_COND_NOTBETWEEN ) + if ( ScGlobal::GetCollator()->compareString( aUpVal1, aUpVal2 ) + == COMPARE_GREATER ) + { + // richtige Reihenfolge fuer Wertebereich + String aTemp( aUpVal1 ); aUpVal1 = aUpVal2; aUpVal2 = aTemp; + } + + BOOL bValid; + switch ( eOp ) + { + case SC_COND_EQUAL: + bValid = (ScGlobal::GetCollator()->compareString( + rArg, aUpVal1 ) == COMPARE_EQUAL); + break; + case SC_COND_NOTEQUAL: + bValid = (ScGlobal::GetCollator()->compareString( + rArg, aUpVal1 ) != COMPARE_EQUAL); + break; + default: + { + sal_Int32 nCompare = ScGlobal::GetCollator()->compareString( + rArg, aUpVal1 ); + switch ( eOp ) + { + case SC_COND_GREATER: + bValid = ( nCompare == COMPARE_GREATER ); + break; + case SC_COND_EQGREATER: + bValid = ( nCompare == COMPARE_EQUAL || nCompare == COMPARE_GREATER ); + break; + case SC_COND_LESS: + bValid = ( nCompare == COMPARE_LESS ); + break; + case SC_COND_EQLESS: + bValid = ( nCompare == COMPARE_EQUAL || nCompare == COMPARE_LESS ); + break; + case SC_COND_BETWEEN: + case SC_COND_NOTBETWEEN: + // Test auf NOTBETWEEN: + bValid = ( nCompare == COMPARE_LESS || + ScGlobal::GetCollator()->compareString( rArg, + aUpVal2 ) == COMPARE_GREATER ); + if ( eOp == SC_COND_BETWEEN ) + bValid = !bValid; + break; + // SC_COND_DIRECT schon oben abgefragt + default: + DBG_ERROR("unbekannte Operation bei ScConditionEntry"); + bValid = FALSE; + break; + } + } + } + return bValid; +} + +BOOL ScConditionEntry::IsCellValid( ScBaseCell* pCell, const ScAddress& rPos ) const +{ + ((ScConditionEntry*)this)->Interpret(rPos); // Formeln auswerten + + double nArg = 0.0; + String aArgStr; + BOOL bVal = TRUE; + + if ( pCell ) + { + CellType eType = pCell->GetCellType(); + switch (eType) + { + case CELLTYPE_VALUE: + nArg = ((ScValueCell*)pCell)->GetValue(); + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + bVal = pFCell->IsValue(); + if (bVal) + nArg = pFCell->GetValue(); + else + pFCell->GetString(aArgStr); + } + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + bVal = FALSE; + if ( eType == CELLTYPE_STRING ) + ((ScStringCell*)pCell)->GetString(aArgStr); + else + ((ScEditCell*)pCell)->GetString(aArgStr); + break; + + default: + pCell = NULL; // Note-Zellen wie leere + break; + } + } + + if (!pCell) + if (bIsStr1) + bVal = FALSE; // leere Zellen je nach Bedingung + + if (bVal) + return IsValid( nArg ); + else + return IsValidStr( aArgStr ); +} + +String ScConditionEntry::GetExpression( const ScAddress& rCursor, USHORT nIndex, + ULONG nNumFmt, + const FormulaGrammar::Grammar eGrammar ) const +{ + String aRet; + + if ( FormulaGrammar::isEnglish( eGrammar) && nNumFmt == 0 ) + nNumFmt = pDoc->GetFormatTable()->GetStandardIndex( LANGUAGE_ENGLISH_US ); + + if ( nIndex==0 ) + { + if ( pFormula1 ) + { + ScCompiler aComp(pDoc, rCursor, *pFormula1); + aComp.SetGrammar(eGrammar); + aComp.CreateStringFromTokenArray( aRet ); + } + else if (bIsStr1) + { + aRet = '"'; + aRet += aStrVal1; + aRet += '"'; + } + else + pDoc->GetFormatTable()->GetInputLineString(nVal1, nNumFmt, aRet); + } + else if ( nIndex==1 ) + { + if ( pFormula2 ) + { + ScCompiler aComp(pDoc, rCursor, *pFormula2); + aComp.SetGrammar(eGrammar); + aComp.CreateStringFromTokenArray( aRet ); + } + else if (bIsStr2) + { + aRet = '"'; + aRet += aStrVal2; + aRet += '"'; + } + else + pDoc->GetFormatTable()->GetInputLineString(nVal2, nNumFmt, aRet); + } + else + { + DBG_ERROR("GetExpression: falscher Index"); + } + + return aRet; +} + +ScTokenArray* ScConditionEntry::CreateTokenArry( USHORT nIndex ) const +{ + ScTokenArray* pRet = NULL; + ScAddress aAddr; + + if ( nIndex==0 ) + { + if ( pFormula1 ) + pRet = new ScTokenArray( *pFormula1 ); + else + { + pRet = new ScTokenArray(); + if (bIsStr1) + pRet->AddString( aStrVal1.GetBuffer() ); + else + pRet->AddDouble( nVal1 ); + } + } + else if ( nIndex==1 ) + { + if ( pFormula2 ) + pRet = new ScTokenArray( *pFormula2 ); + else + { + pRet = new ScTokenArray(); + if (bIsStr2) + pRet->AddString( aStrVal2.GetBuffer() ); + else + pRet->AddDouble( nVal2 ); + } + } + else + { + DBG_ERROR("GetExpression: falscher Index"); + } + + return pRet; +} + +void ScConditionEntry::SourceChanged( const ScAddress& rChanged ) +{ + for (USHORT nPass = 0; nPass < 2; nPass++) + { + ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; + if (pFormula) + { + pFormula->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pFormula->GetNextReference()) ) != NULL ) + { + SingleDoubleRefProvider aProv( *t ); + if ( aProv.Ref1.IsColRel() || aProv.Ref1.IsRowRel() || aProv.Ref1.IsTabRel() || + aProv.Ref2.IsColRel() || aProv.Ref2.IsRowRel() || aProv.Ref2.IsTabRel() ) + { + // absolut muss getroffen sein, relativ bestimmt Bereich + + BOOL bHit = TRUE; + SCsCOL nCol1; + SCsROW nRow1; + SCsTAB nTab1; + SCsCOL nCol2; + SCsROW nRow2; + SCsTAB nTab2; + + if ( aProv.Ref1.IsColRel() ) + nCol2 = rChanged.Col() - aProv.Ref1.nRelCol; + else + { + bHit &= ( rChanged.Col() >= aProv.Ref1.nCol ); + nCol2 = MAXCOL; + } + if ( aProv.Ref1.IsRowRel() ) + nRow2 = rChanged.Row() - aProv.Ref1.nRelRow; + else + { + bHit &= ( rChanged.Row() >= aProv.Ref1.nRow ); + nRow2 = MAXROW; + } + if ( aProv.Ref1.IsTabRel() ) + nTab2 = rChanged.Tab() - aProv.Ref1.nRelTab; + else + { + bHit &= ( rChanged.Tab() >= aProv.Ref1.nTab ); + nTab2 = MAXTAB; + } + + if ( aProv.Ref2.IsColRel() ) + nCol1 = rChanged.Col() - aProv.Ref2.nRelCol; + else + { + bHit &= ( rChanged.Col() <= aProv.Ref2.nCol ); + nCol1 = 0; + } + if ( aProv.Ref2.IsRowRel() ) + nRow1 = rChanged.Row() - aProv.Ref2.nRelRow; + else + { + bHit &= ( rChanged.Row() <= aProv.Ref2.nRow ); + nRow1 = 0; + } + if ( aProv.Ref2.IsTabRel() ) + nTab1 = rChanged.Tab() - aProv.Ref2.nRelTab; + else + { + bHit &= ( rChanged.Tab() <= aProv.Ref2.nTab ); + nTab1 = 0; + } + + if ( bHit ) + { + //! begrenzen + + ScRange aPaint( nCol1,nRow1,nTab1, nCol2,nRow2,nTab2 ); + + // kein Paint, wenn es nur die Zelle selber ist + if ( aPaint.aStart != rChanged || aPaint.aEnd != rChanged ) + DataChanged( &aPaint ); + } + } + } + } + } +} + +ScAddress ScConditionEntry::GetValidSrcPos() const +{ + // return a position that's adjusted to allow textual representation of expressions if possible + + SCTAB nMinTab = aSrcPos.Tab(); + SCTAB nMaxTab = nMinTab; + + for (USHORT nPass = 0; nPass < 2; nPass++) + { + ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; + if (pFormula) + { + pFormula->Reset(); + ScToken* t; + while ( ( t = static_cast<ScToken*>(pFormula->GetNextReference()) ) != NULL ) + { + ScSingleRefData& rRef1 = t->GetSingleRef(); + if ( rRef1.IsTabRel() && !rRef1.IsTabDeleted() ) + { + if ( rRef1.nTab < nMinTab ) + nMinTab = rRef1.nTab; + if ( rRef1.nTab > nMaxTab ) + nMaxTab = rRef1.nTab; + } + if ( t->GetType() == svDoubleRef ) + { + ScSingleRefData& rRef2 = t->GetDoubleRef().Ref2; + if ( rRef2.IsTabRel() && !rRef2.IsTabDeleted() ) + { + if ( rRef2.nTab < nMinTab ) + nMinTab = rRef2.nTab; + if ( rRef2.nTab > nMaxTab ) + nMaxTab = rRef2.nTab; + } + } + } + } + } + + ScAddress aValidPos = aSrcPos; + SCTAB nTabCount = pDoc->GetTableCount(); + if ( nMaxTab >= nTabCount && nMinTab > 0 ) + aValidPos.SetTab( aSrcPos.Tab() - nMinTab ); // so the lowest tab ref will be on 0 + + if ( aValidPos.Tab() >= nTabCount ) + aValidPos.SetTab( nTabCount - 1 ); // ensure a valid position even if some references will be invalid + + return aValidPos; +} + +void ScConditionEntry::DataChanged( const ScRange* /* pModified */ ) const +{ + // nix +} + +bool ScConditionEntry::MarkUsedExternalReferences() const +{ + bool bAllMarked = false; + for (USHORT nPass = 0; !bAllMarked && nPass < 2; nPass++) + { + ScTokenArray* pFormula = nPass ? pFormula2 : pFormula1; + if (pFormula) + bAllMarked = pDoc->MarkUsedExternalReferences( *pFormula); + } + return bAllMarked; +} + +//------------------------------------------------------------------------ + +ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper, + const String& rExpr1, const String& rExpr2, + ScDocument* pDocument, const ScAddress& rPos, + const String& rStyle, + const String& rExprNmsp1, const String& rExprNmsp2, + FormulaGrammar::Grammar eGrammar1, + FormulaGrammar::Grammar eGrammar2 ) : + ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ), + aStyleName( rStyle ), + pParent( NULL ) +{ +} + +ScCondFormatEntry::ScCondFormatEntry( ScConditionMode eOper, + const ScTokenArray* pArr1, const ScTokenArray* pArr2, + ScDocument* pDocument, const ScAddress& rPos, + const String& rStyle ) : + ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ), + aStyleName( rStyle ), + pParent( NULL ) +{ +} + +ScCondFormatEntry::ScCondFormatEntry( const ScCondFormatEntry& r ) : + ScConditionEntry( r ), + aStyleName( r.aStyleName ), + pParent( NULL ) +{ +} + +ScCondFormatEntry::ScCondFormatEntry( ScDocument* pDocument, const ScCondFormatEntry& r ) : + ScConditionEntry( pDocument, r ), + aStyleName( r.aStyleName ), + pParent( NULL ) +{ +} + +int ScCondFormatEntry::operator== ( const ScCondFormatEntry& r ) const +{ + return ScConditionEntry::operator==( r ) && + aStyleName == r.aStyleName; + + // Range wird nicht verglichen +} + +ScCondFormatEntry::~ScCondFormatEntry() +{ +} + +void ScCondFormatEntry::DataChanged( const ScRange* pModified ) const +{ + if ( pParent ) + pParent->DoRepaint( pModified ); +} + +//------------------------------------------------------------------------ + +ScConditionalFormat::ScConditionalFormat(sal_uInt32 nNewKey, ScDocument* pDocument) : + pDoc( pDocument ), + pAreas( NULL ), + nKey( nNewKey ), + ppEntries( NULL ), + nEntryCount( 0 ) +{ +} + +ScConditionalFormat::ScConditionalFormat(const ScConditionalFormat& r) : + pDoc( r.pDoc ), + pAreas( NULL ), + nKey( r.nKey ), + ppEntries( NULL ), + nEntryCount( r.nEntryCount ) +{ + if (nEntryCount) + { + ppEntries = new ScCondFormatEntry*[nEntryCount]; + for (USHORT i=0; i<nEntryCount; i++) + { + ppEntries[i] = new ScCondFormatEntry(*r.ppEntries[i]); + ppEntries[i]->SetParent(this); + } + } +} + +ScConditionalFormat* ScConditionalFormat::Clone(ScDocument* pNewDoc) const +{ + // echte Kopie der Formeln (fuer Ref-Undo / zwischen Dokumenten) + + if (!pNewDoc) + pNewDoc = pDoc; + + ScConditionalFormat* pNew = new ScConditionalFormat(nKey, pNewDoc); + DBG_ASSERT(!pNew->ppEntries, "wo kommen die Eintraege her?"); + + if (nEntryCount) + { + pNew->ppEntries = new ScCondFormatEntry*[nEntryCount]; + for (USHORT i=0; i<nEntryCount; i++) + { + pNew->ppEntries[i] = new ScCondFormatEntry( pNewDoc, *ppEntries[i] ); + pNew->ppEntries[i]->SetParent(pNew); + } + pNew->nEntryCount = nEntryCount; + } + + return pNew; +} + +BOOL ScConditionalFormat::EqualEntries( const ScConditionalFormat& r ) const +{ + if ( nEntryCount != r.nEntryCount ) + return FALSE; + + //! auf gleiche Eintraege in anderer Reihenfolge testen ??? + + for (USHORT i=0; i<nEntryCount; i++) + if ( ! (*ppEntries[i] == *r.ppEntries[i]) ) + return FALSE; + + return TRUE; +} + +void ScConditionalFormat::AddEntry( const ScCondFormatEntry& rNew ) +{ + ScCondFormatEntry** ppNew = new ScCondFormatEntry*[nEntryCount+1]; + for (USHORT i=0; i<nEntryCount; i++) + ppNew[i] = ppEntries[i]; + ppNew[nEntryCount] = new ScCondFormatEntry(rNew); + ppNew[nEntryCount]->SetParent(this); + ++nEntryCount; + delete[] ppEntries; + ppEntries = ppNew; +} + +ScConditionalFormat::~ScConditionalFormat() +{ + for (USHORT i=0; i<nEntryCount; i++) + delete ppEntries[i]; + delete[] ppEntries; + + delete pAreas; +} + +const ScCondFormatEntry* ScConditionalFormat::GetEntry( USHORT nPos ) const +{ + if ( nPos < nEntryCount ) + return ppEntries[nPos]; + else + return NULL; +} + +const String& ScConditionalFormat::GetCellStyle( ScBaseCell* pCell, const ScAddress& rPos ) const +{ + for (USHORT i=0; i<nEntryCount; i++) + if ( ppEntries[i]->IsCellValid( pCell, rPos ) ) + return ppEntries[i]->GetStyle(); + + return EMPTY_STRING; +} + +void lcl_Extend( ScRange& rRange, ScDocument* pDoc, BOOL bLines ) +{ + SCTAB nTab = rRange.aStart.Tab(); + DBG_ASSERT(rRange.aEnd.Tab() == nTab, "lcl_Extend - mehrere Tabellen?"); + + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + + BOOL bEx = pDoc->ExtendMerge( nStartCol, nStartRow, nEndCol, nEndRow, nTab ); + + if (bLines) + { + if (nStartCol > 0) --nStartCol; + if (nStartRow > 0) --nStartRow; + if (nEndCol < MAXCOL) ++nEndCol; + if (nEndRow < MAXROW) ++nEndRow; + } + + if ( bEx || bLines ) + { + rRange.aStart.Set( nStartCol, nStartRow, nTab ); + rRange.aEnd.Set( nEndCol, nEndRow, nTab ); + } +} + +BOOL lcl_CutRange( ScRange& rRange, const ScRange& rOther ) +{ + rRange.Justify(); + ScRange aCmpRange = rOther; + aCmpRange.Justify(); + + if ( rRange.aStart.Col() <= aCmpRange.aEnd.Col() && + rRange.aEnd.Col() >= aCmpRange.aStart.Col() && + rRange.aStart.Row() <= aCmpRange.aEnd.Row() && + rRange.aEnd.Row() >= aCmpRange.aStart.Row() && + rRange.aStart.Tab() <= aCmpRange.aEnd.Tab() && + rRange.aEnd.Tab() >= aCmpRange.aStart.Tab() ) + { + if ( rRange.aStart.Col() < aCmpRange.aStart.Col() ) + rRange.aStart.SetCol( aCmpRange.aStart.Col() ); + if ( rRange.aStart.Row() < aCmpRange.aStart.Row() ) + rRange.aStart.SetRow( aCmpRange.aStart.Row() ); + if ( rRange.aStart.Tab() < aCmpRange.aStart.Tab() ) + rRange.aStart.SetTab( aCmpRange.aStart.Tab() ); + if ( rRange.aEnd.Col() > aCmpRange.aEnd.Col() ) + rRange.aEnd.SetCol( aCmpRange.aEnd.Col() ); + if ( rRange.aEnd.Row() > aCmpRange.aEnd.Row() ) + rRange.aEnd.SetRow( aCmpRange.aEnd.Row() ); + if ( rRange.aEnd.Tab() > aCmpRange.aEnd.Tab() ) + rRange.aEnd.SetTab( aCmpRange.aEnd.Tab() ); + + return TRUE; + } + + return FALSE; // ausserhalb +} + +void ScConditionalFormat::DoRepaint( const ScRange* pModified ) +{ + USHORT i; + SfxObjectShell* pSh = pDoc->GetDocumentShell(); + if (pSh) + { + // Rahmen/Schatten enthalten? + // (alle Bedingungen testen) + BOOL bExtend = FALSE; + BOOL bRotate = FALSE; + BOOL bAttrTested = FALSE; + + if (!pAreas) // RangeList ggf. holen + { + pAreas = new ScRangeList; + pDoc->FindConditionalFormat( nKey, *pAreas ); + } + USHORT nCount = (USHORT) pAreas->Count(); + for (i=0; i<nCount; i++) + { + ScRange aRange = *pAreas->GetObject(i); + BOOL bDo = TRUE; + if ( pModified ) + { + if ( !lcl_CutRange( aRange, *pModified ) ) + bDo = FALSE; + } + if (bDo) + { + if ( !bAttrTested ) + { + // #116562# Look at the style's content only if the repaint is necessary + // for any condition, to avoid the time-consuming Find() if there are many + // conditional formats and styles. + for (USHORT nEntry=0; nEntry<nEntryCount; nEntry++) + { + String aStyle = ppEntries[nEntry]->GetStyle(); + if (aStyle.Len()) + { + SfxStyleSheetBase* pStyleSheet = + pDoc->GetStyleSheetPool()->Find( aStyle, SFX_STYLE_FAMILY_PARA ); + if ( pStyleSheet ) + { + const SfxItemSet& rSet = pStyleSheet->GetItemSet(); + if (rSet.GetItemState( ATTR_BORDER, TRUE ) == SFX_ITEM_SET || + rSet.GetItemState( ATTR_SHADOW, TRUE ) == SFX_ITEM_SET) + { + bExtend = TRUE; + } + if (rSet.GetItemState( ATTR_ROTATE_VALUE, TRUE ) == SFX_ITEM_SET || + rSet.GetItemState( ATTR_ROTATE_MODE, TRUE ) == SFX_ITEM_SET) + { + bRotate = TRUE; + } + } + } + } + bAttrTested = TRUE; + } + + lcl_Extend( aRange, pDoc, bExtend ); // zusammengefasste und bExtend + if ( bRotate ) + { + aRange.aStart.SetCol(0); + aRange.aEnd.SetCol(MAXCOL); // gedreht: ganze Zeilen + } + + // gedreht -> ganze Zeilen + if ( aRange.aStart.Col() != 0 || aRange.aEnd.Col() != MAXCOL ) + { + if ( pDoc->HasAttrib( 0,aRange.aStart.Row(),aRange.aStart.Tab(), + MAXCOL,aRange.aEnd.Row(),aRange.aEnd.Tab(), + HASATTR_ROTATE ) ) + { + aRange.aStart.SetCol(0); + aRange.aEnd.SetCol(MAXCOL); + } + } + + pSh->Broadcast( ScPaintHint( aRange, PAINT_GRID ) ); + } + } + } +} + +void ScConditionalFormat::InvalidateArea() +{ + delete pAreas; + pAreas = NULL; +} + +void ScConditionalFormat::CompileAll() +{ + for (USHORT i=0; i<nEntryCount; i++) + ppEntries[i]->CompileAll(); +} + +void ScConditionalFormat::CompileXML() +{ + for (USHORT i=0; i<nEntryCount; i++) + ppEntries[i]->CompileXML(); +} + +void ScConditionalFormat::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + for (USHORT i=0; i<nEntryCount; i++) + ppEntries[i]->UpdateReference(eUpdateRefMode, rRange, nDx, nDy, nDz); + + delete pAreas; // aus dem AttrArray kommt beim Einfuegen/Loeschen kein Aufruf + pAreas = NULL; +} + +void ScConditionalFormat::RenameCellStyle(const String& rOld, const String& rNew) +{ + for (USHORT i=0; i<nEntryCount; i++) + if ( ppEntries[i]->GetStyle() == rOld ) + ppEntries[i]->UpdateStyleName( rNew ); +} + +void ScConditionalFormat::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + for (USHORT i=0; i<nEntryCount; i++) + ppEntries[i]->UpdateMoveTab( nOldPos, nNewPos ); + + delete pAreas; // aus dem AttrArray kommt beim Einfuegen/Loeschen kein Aufruf + pAreas = NULL; +} + +void ScConditionalFormat::SourceChanged( const ScAddress& rAddr ) +{ + for (USHORT i=0; i<nEntryCount; i++) + ppEntries[i]->SourceChanged( rAddr ); +} + +bool ScConditionalFormat::MarkUsedExternalReferences() const +{ + bool bAllMarked = false; + for (USHORT i=0; !bAllMarked && i<nEntryCount; i++) + bAllMarked = ppEntries[i]->MarkUsedExternalReferences(); + return bAllMarked; +} + +//------------------------------------------------------------------------ + +ScConditionalFormatList::ScConditionalFormatList(const ScConditionalFormatList& rList) : + ScConditionalFormats_Impl() +{ + // fuer Ref-Undo - echte Kopie mit neuen Tokens! + + USHORT nCount = rList.Count(); + + for (USHORT i=0; i<nCount; i++) + InsertNew( rList[i]->Clone() ); + + //! sortierte Eintraege aus rList schneller einfuegen ??? +} + +ScConditionalFormatList::ScConditionalFormatList(ScDocument* pNewDoc, + const ScConditionalFormatList& rList) +{ + // fuer neues Dokument - echte Kopie mit neuen Tokens! + + USHORT nCount = rList.Count(); + + for (USHORT i=0; i<nCount; i++) + InsertNew( rList[i]->Clone(pNewDoc) ); + + //! sortierte Eintraege aus rList schneller einfuegen ??? +} + +BOOL ScConditionalFormatList::operator==( const ScConditionalFormatList& r ) const +{ + // fuer Ref-Undo - interne Variablen werden nicht verglichen + + USHORT nCount = Count(); + BOOL bEqual = ( nCount == r.Count() ); + for (USHORT i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert + if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? + bEqual = FALSE; + + return bEqual; +} + +ScConditionalFormat* ScConditionalFormatList::GetFormat( sal_uInt32 nKey ) +{ + //! binaer suchen + + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + if ((*this)[i]->GetKey() == nKey) + return (*this)[i]; + + DBG_ERROR("ScConditionalFormatList: Eintrag nicht gefunden"); + return NULL; +} + +void ScConditionalFormatList::CompileAll() +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->CompileAll(); +} + +void ScConditionalFormatList::CompileXML() +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->CompileXML(); +} + +void ScConditionalFormatList::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz ); +} + +void ScConditionalFormatList::RenameCellStyle( const String& rOld, const String& rNew ) +{ + ULONG nCount=Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->RenameCellStyle(rOld,rNew); +} + +void ScConditionalFormatList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); +} + +void ScConditionalFormatList::SourceChanged( const ScAddress& rAddr ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->SourceChanged( rAddr ); +} + +bool ScConditionalFormatList::MarkUsedExternalReferences() const +{ + bool bAllMarked = false; + USHORT nCount = Count(); + for (USHORT i=0; !bAllMarked && i<nCount; i++) + bAllMarked = (*this)[i]->MarkUsedExternalReferences(); + return bAllMarked; +} diff --git a/sc/source/core/data/dbdocutl.cxx b/sc/source/core/data/dbdocutl.cxx new file mode 100644 index 000000000000..6a849282a642 --- /dev/null +++ b/sc/source/core/data/dbdocutl.cxx @@ -0,0 +1,200 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dbdocutl.cxx,v $ + * $Revision: 1.9 $ + * + * 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 <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> + +#include <svtools/zforlist.hxx> + +#include "dbdocutl.hxx" +#include "document.hxx" +#include "cell.hxx" +#include "formula/errorcodes.hxx" + +using namespace ::com::sun::star; + +#define D_TIMEFACTOR 86400.0 + +// ----------------------------------------------------------------------- + +// static +void ScDatabaseDocUtil::PutData( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab, + const uno::Reference<sdbc::XRow>& xRow, long nRowPos, + long nType, BOOL bCurrency, BOOL* pSimpleFlag ) +{ + String aString; + double nVal = 0.0; + BOOL bValue = FALSE; + BOOL bEmptyFlag = FALSE; + BOOL bError = FALSE; + ULONG nFormatIndex = 0; + + //! wasNull calls only if null value was found? + + try + { + switch ( nType ) + { + case sdbc::DataType::BIT: + case sdbc::DataType::BOOLEAN: + //! use language from doc (here, date/time and currency)? + nFormatIndex = pDoc->GetFormatTable()->GetStandardFormat( + NUMBERFORMAT_LOGICAL, ScGlobal::eLnge ); + nVal = (xRow->getBoolean(nRowPos) ? 1 : 0); + bEmptyFlag = ( nVal == 0.0 ) && xRow->wasNull(); + bValue = TRUE; + break; + + case sdbc::DataType::TINYINT: + case sdbc::DataType::SMALLINT: + case sdbc::DataType::INTEGER: + case sdbc::DataType::BIGINT: + case sdbc::DataType::FLOAT: + case sdbc::DataType::REAL: + case sdbc::DataType::DOUBLE: + case sdbc::DataType::NUMERIC: + case sdbc::DataType::DECIMAL: + //! do the conversion here? + nVal = xRow->getDouble(nRowPos); + bEmptyFlag = ( nVal == 0.0 ) && xRow->wasNull(); + bValue = TRUE; + break; + + case sdbc::DataType::CHAR: + case sdbc::DataType::VARCHAR: + case sdbc::DataType::LONGVARCHAR: + aString = xRow->getString(nRowPos); + bEmptyFlag = ( aString.Len() == 0 ) && xRow->wasNull(); + break; + + case sdbc::DataType::DATE: + { + SvNumberFormatter* pFormTable = pDoc->GetFormatTable(); + nFormatIndex = pFormTable->GetStandardFormat( + NUMBERFORMAT_DATE, ScGlobal::eLnge ); + + util::Date aDate = xRow->getDate(nRowPos); + nVal = Date( aDate.Day, aDate.Month, aDate.Year ) - + *pFormTable->GetNullDate(); + bEmptyFlag = xRow->wasNull(); + bValue = TRUE; + } + break; + + case sdbc::DataType::TIME: + { + SvNumberFormatter* pFormTable = pDoc->GetFormatTable(); + nFormatIndex = pFormTable->GetStandardFormat( + NUMBERFORMAT_TIME, ScGlobal::eLnge ); + + util::Time aTime = xRow->getTime(nRowPos); + nVal = ( aTime.Hours * 3600 + aTime.Minutes * 60 + + aTime.Seconds + aTime.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; + bEmptyFlag = xRow->wasNull(); + bValue = TRUE; + } + break; + + case sdbc::DataType::TIMESTAMP: + { + SvNumberFormatter* pFormTable = pDoc->GetFormatTable(); + nFormatIndex = pFormTable->GetStandardFormat( + NUMBERFORMAT_DATETIME, ScGlobal::eLnge ); + + util::DateTime aStamp = xRow->getTimestamp(nRowPos); + nVal = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - + *pFormTable->GetNullDate() ) + + ( aStamp.Hours * 3600 + aStamp.Minutes * 60 + + aStamp.Seconds + aStamp.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; + bEmptyFlag = xRow->wasNull(); + bValue = TRUE; + } + break; + + case sdbc::DataType::SQLNULL: + bEmptyFlag = TRUE; + break; + + case sdbc::DataType::BINARY: + case sdbc::DataType::VARBINARY: + case sdbc::DataType::LONGVARBINARY: + default: + bError = TRUE; // unknown type + } + } + catch ( uno::Exception& ) + { + bError = TRUE; + } + + if ( bValue && bCurrency ) + nFormatIndex = pDoc->GetFormatTable()->GetStandardFormat( + NUMBERFORMAT_CURRENCY, ScGlobal::eLnge ); + + ScBaseCell* pCell; + if (bEmptyFlag) + { + pCell = NULL; + pDoc->PutCell( nCol, nRow, nTab, pCell ); + } + else if (bError) + { + pDoc->SetError( nCol, nRow, nTab, NOTAVAILABLE ); + } + else if (bValue) + { + pCell = new ScValueCell( nVal ); + if (nFormatIndex == 0) + pDoc->PutCell( nCol, nRow, nTab, pCell ); + else + pDoc->PutCell( nCol, nRow, nTab, pCell, nFormatIndex ); + } + else + { + if (aString.Len()) + { + pCell = ScBaseCell::CreateTextCell( aString, pDoc ); + if ( pSimpleFlag && pCell->GetCellType() == CELLTYPE_EDIT ) + *pSimpleFlag = FALSE; + } + else + pCell = NULL; + pDoc->PutCell( nCol, nRow, nTab, pCell ); + } +} + + diff --git a/sc/source/core/data/dociter.cxx b/sc/source/core/data/dociter.cxx new file mode 100644 index 000000000000..b88cd49a6a8c --- /dev/null +++ b/sc/source/core/data/dociter.cxx @@ -0,0 +1,1803 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dociter.cxx,v $ + * $Revision: 1.22.88.2 $ + * + * 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 <svtools/zforlist.hxx> + +#include "scitems.hxx" +#include "global.hxx" +#include "dociter.hxx" +#include "document.hxx" +#include "table.hxx" +#include "column.hxx" +#include "cell.hxx" +#include "attarray.hxx" +#include "patattr.hxx" +#include "docoptio.hxx" +#include "cellform.hxx" + + +// STATIC DATA ----------------------------------------------------------- + +ScDocumentIterator::ScDocumentIterator( ScDocument* pDocument, + SCTAB nStartTable, SCTAB nEndTable ) : + pDoc( pDocument ), + nStartTab( nStartTable ), + nEndTab( nEndTable ) +{ + PutInOrder( nStartTab, nEndTab ); + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + pDefPattern = pDoc->GetDefPattern(); + + nCol = 0; + nRow = 0; + nTab = nStartTab; + + nColPos = 0; + nAttrPos = 0; +} + +ScDocumentIterator::~ScDocumentIterator() +{ +} + +BOOL ScDocumentIterator::GetThisCol() +{ + ScTable* pTab; + while ( (pTab = pDoc->pTab[nTab]) == NULL ) + { + if ( nTab == nEndTab ) + { + nCol = MAXCOL; + nRow = MAXROW; + return FALSE; + } + ++nTab; + } + ScColumn* pCol = &pTab->aCol[nCol]; + ScAttrArray* pAtt = pCol->pAttrArray; + + BOOL bFound = FALSE; + do + { + SCROW nColRow; + SCROW nAttrEnd; + + do + { + nAttrEnd = pAtt->pData[nAttrPos].nRow; + if (nAttrEnd < nRow) + ++nAttrPos; + } + while (nAttrEnd < nRow); + + do + { + nColRow = (nColPos < pCol->nCount) ? pCol->pItems[nColPos].nRow : MAXROW+1; + if (nColRow < nRow) + ++nColPos; + } + while (nColRow < nRow); + + if (nColRow == nRow) + { + bFound = TRUE; + pCell = pCol->pItems[nColPos].pCell; + pPattern = pAtt->pData[nAttrPos].pPattern; + } + else if ( pAtt->pData[nAttrPos].pPattern != pDefPattern ) + { + bFound = TRUE; + pCell = NULL; + pPattern = pAtt->pData[nAttrPos].pPattern; + } + else + { + nRow = Min( (SCROW)nColRow, (SCROW)(nAttrEnd+1) ); + } + } + while (!bFound && nRow <= MAXROW); + + return bFound; +} + +BOOL ScDocumentIterator::GetThis() +{ + BOOL bEnd = FALSE; + BOOL bSuccess = FALSE; + + while ( !bSuccess && !bEnd ) + { + if ( nRow > MAXROW ) + bSuccess = FALSE; + else + bSuccess = GetThisCol(); + + if ( !bSuccess ) + { + ++nCol; + if (nCol > MAXCOL) + { + nCol = 0; + ++nTab; + if (nTab > nEndTab) + bEnd = TRUE; + } + nRow = 0; + nColPos = 0; + nAttrPos = 0; + } + } + + return !bEnd; +} + +BOOL ScDocumentIterator::GetFirst() +{ + nCol = 0; + nTab = nStartTab; + + nRow = 0; + nColPos = 0; + nAttrPos = 0; + + return GetThis(); +} + +BOOL ScDocumentIterator::GetNext() +{ + ++nRow; + + return GetThis(); +} + +//------------------------------------------------------------------------ + +ScBaseCell* ScDocumentIterator::GetCell() +{ + return pCell; +} + +const ScPatternAttr* ScDocumentIterator::GetPattern() +{ + return pPattern; +} + +void ScDocumentIterator::GetPos( SCCOL& rCol, SCROW& rRow, SCTAB& rTab ) +{ + rCol = nCol; + rRow = nRow; + rTab = nTab; +} + + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ +void lcl_IterGetNumberFormat( ULONG& nFormat, const ScAttrArray*& rpArr, + SCROW& nAttrEndRow, const ScAttrArray* pNewArr, SCROW nRow, + ScDocument* pDoc ) +{ + if ( rpArr != pNewArr || nAttrEndRow < nRow ) + { + SCSIZE nPos; + pNewArr->Search( nRow, nPos ); // nPos 0 gueltig wenn nicht gefunden + const ScPatternAttr* pPattern = pNewArr->pData[nPos].pPattern; + nFormat = pPattern->GetNumberFormat( pDoc->GetFormatTable() ); + rpArr = pNewArr; + nAttrEndRow = pNewArr->pData[nPos].nRow; + } +} + +//UNUSED2008-05 ScValueIterator::ScValueIterator( ScDocument* pDocument, +//UNUSED2008-05 SCCOL nSCol, SCROW nSRow, SCTAB nSTab, +//UNUSED2008-05 SCCOL nECol, SCROW nERow, SCTAB nETab, +//UNUSED2008-05 BOOL bSTotal, BOOL bTextZero ) : +//UNUSED2008-05 pDoc( pDocument ), +//UNUSED2008-05 nNumFmtIndex(0), +//UNUSED2008-05 nStartCol( nSCol), +//UNUSED2008-05 nStartRow( nSRow), +//UNUSED2008-05 nStartTab( nSTab ), +//UNUSED2008-05 nEndCol( nECol ), +//UNUSED2008-05 nEndRow( nERow), +//UNUSED2008-05 nEndTab( nETab ), +//UNUSED2008-05 nNumFmtType( NUMBERFORMAT_UNDEFINED ), +//UNUSED2008-05 bNumValid( FALSE ), +//UNUSED2008-05 bSubTotal(bSTotal), +//UNUSED2008-05 bNextValid( FALSE ), +//UNUSED2008-05 bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), +//UNUSED2008-05 bTextAsZero( bTextZero ) +//UNUSED2008-05 { +//UNUSED2008-05 PutInOrder( nStartCol, nEndCol); +//UNUSED2008-05 PutInOrder( nStartRow, nEndRow); +//UNUSED2008-05 PutInOrder( nStartTab, nEndTab ); +//UNUSED2008-05 +//UNUSED2008-05 if (!ValidCol(nStartCol)) nStartCol = MAXCOL; +//UNUSED2008-05 if (!ValidCol(nEndCol)) nEndCol = MAXCOL; +//UNUSED2008-05 if (!ValidRow(nStartRow)) nStartRow = MAXROW; +//UNUSED2008-05 if (!ValidRow(nEndRow)) nEndRow = MAXROW; +//UNUSED2008-05 if (!ValidTab(nStartTab)) nStartTab = MAXTAB; +//UNUSED2008-05 if (!ValidTab(nEndTab)) nEndTab = MAXTAB; +//UNUSED2008-05 +//UNUSED2008-05 nCol = nStartCol; +//UNUSED2008-05 nRow = nStartRow; +//UNUSED2008-05 nTab = nStartTab; +//UNUSED2008-05 +//UNUSED2008-05 nColRow = 0; // wird bei GetFirst initialisiert +//UNUSED2008-05 +//UNUSED2008-05 nNumFormat = 0; // werden bei GetNumberFormat initialisiert +//UNUSED2008-05 pAttrArray = 0; +//UNUSED2008-05 nAttrEndRow = 0; +//UNUSED2008-05 } + +ScValueIterator::ScValueIterator( ScDocument* pDocument, const ScRange& rRange, + BOOL bSTotal, BOOL bTextZero ) : + pDoc( pDocument ), + nNumFmtIndex(0), + nStartCol( rRange.aStart.Col() ), + nStartRow( rRange.aStart.Row() ), + nStartTab( rRange.aStart.Tab() ), + nEndCol( rRange.aEnd.Col() ), + nEndRow( rRange.aEnd.Row() ), + nEndTab( rRange.aEnd.Tab() ), + nNumFmtType( NUMBERFORMAT_UNDEFINED ), + bNumValid( FALSE ), + bSubTotal(bSTotal), + bNextValid( FALSE ), + bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ), + bTextAsZero( bTextZero ) +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + + nColRow = 0; // wird bei GetFirst initialisiert + + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +BOOL ScValueIterator::GetThis(double& rValue, USHORT& rErr) +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + for (;;) + { + if ( nRow > nEndRow ) + { + nRow = nStartRow; + do + { + nCol++; + if ( nCol > nEndCol ) + { + nCol = nStartCol; + nTab++; + if ( nTab > nEndTab ) + { + // rValue = 0.0; //! do not change caller's value! + rErr = 0; + return FALSE; // Ende und Aus + } + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + } + + while (( nColRow < pCol->nCount ) && ( pCol->pItems[nColRow].nRow < nRow )) + nColRow++; + + if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) + { + nRow = pCol->pItems[nColRow].nRow + 1; + if ( !bSubTotal || !pDoc->pTab[nTab]->IsFiltered( nRow-1 ) ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + ++nColRow; + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + { + bNumValid = FALSE; + rValue = ((ScValueCell*)pCell)->GetValue(); + rErr = 0; + --nRow; + if ( bCalcAsShown ) + { + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); + rValue = pDoc->RoundValueAsShown( rValue, nNumFormat ); + } + // + // wenn in der selben Spalte gleich noch eine Value-Cell folgt, die + // auch noch im Block liegt, den Wert jetzt schon holen + // + if ( nColRow < pCol->nCount && + pCol->pItems[nColRow].nRow <= nEndRow && + pCol->pItems[nColRow].pCell->GetCellType() == CELLTYPE_VALUE && + !bSubTotal ) + { + fNextValue = ((ScValueCell*)pCol->pItems[nColRow].pCell)->GetValue(); + nNextRow = pCol->pItems[nColRow].nRow; + bNextValid = TRUE; + if ( bCalcAsShown ) + { + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nNextRow, pDoc ); + fNextValue = pDoc->RoundValueAsShown( fNextValue, nNumFormat ); + } + } + + return TRUE; // gefunden + } +// break; + case CELLTYPE_FORMULA: + { + if (!bSubTotal || !((ScFormulaCell*)pCell)->IsSubTotal()) + { + rErr = ((ScFormulaCell*)pCell)->GetErrCode(); + if ( rErr || ((ScFormulaCell*)pCell)->IsValue() ) + { + rValue = ((ScFormulaCell*)pCell)->GetValue(); + nRow--; + bNumValid = FALSE; + return TRUE; // gefunden + } + else if ( bTextAsZero ) + { + rValue = 0.0; + nRow--; + bNumValid = FALSE; + return TRUE; + } + } + } + break; + case CELLTYPE_STRING : + case CELLTYPE_EDIT : + { + if ( bTextAsZero ) + { + rErr = 0; + rValue = 0.0; + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + bNumValid = TRUE; + --nRow; + return TRUE; + } + } + break; + default: + { + // added to avoid warnings + } + } + } + } + else + nRow = nEndRow + 1; // naechste Spalte + } +} + +void ScValueIterator::GetCurNumFmtInfo( short& nType, ULONG& nIndex ) +{ + if (!bNumValid) + { + const ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + nNumFmtIndex = pCol->GetNumberFormat( nRow ); + if ( (nNumFmtIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0 ) + { + const ScBaseCell* pCell; + SCSIZE nIdx = nColRow - 1; + // there might be rearranged something, so be on the safe side + if ( nIdx < pCol->nCount && pCol->pItems[nIdx].nRow == nRow ) + pCell = pCol->pItems[nIdx].pCell; + else + { + if ( pCol->Search( nRow, nIdx ) ) + pCell = pCol->pItems[nIdx].pCell; + else + pCell = NULL; + } + if ( pCell && pCell->GetCellType() == CELLTYPE_FORMULA ) + ((const ScFormulaCell*)pCell)->GetFormatInfo( nNumFmtType, nNumFmtIndex ); + else + nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); + } + else + nNumFmtType = pDoc->GetFormatTable()->GetType( nNumFmtIndex ); + bNumValid = TRUE; + } + nType = nNumFmtType; + nIndex = nNumFmtIndex; +} + +BOOL ScValueIterator::GetFirst(double& rValue, USHORT& rErr) +{ + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; + + return GetThis(rValue, rErr); +} + +/* ist inline: +BOOL ScValueIterator::GetNext(double& rValue, USHORT& rErr) +{ + ++nRow; + return GetThis(rValue, rErr); +} +*/ + +//------------------------------------------------------------------------ +//------------------------------------------------------------------------ + +ScQueryValueIterator::ScQueryValueIterator(ScDocument* pDocument, SCTAB nTable, const ScQueryParam& rParam) : + aParam (rParam), + pDoc( pDocument ), + nNumFmtIndex(0), + nTab( nTable), + nNumFmtType( NUMBERFORMAT_UNDEFINED ), + bCalcAsShown( pDocument->GetDocOptions().IsCalcAsShown() ) +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + nColRow = 0; // wird bei GetFirst initialisiert + SCSIZE i; + SCSIZE nCount = aParam.GetEntryCount(); + for (i=0; (i<nCount) && (aParam.GetEntry(i).bDoQuery); i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pDoc->GetFormatTable()->IsNumberFormat(*rEntry.pStr, nIndex, rEntry.nVal)); + } + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +BOOL ScQueryValueIterator::GetThis(double& rValue, USHORT& rErr) +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + SCCOLROW nFirstQueryField = aParam.GetEntry(0).nField; + for ( ;; ) + { + if ( nRow > aParam.nRow2 ) + { + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; + do + { + nCol++; + if ( nCol > aParam.nCol2 ) + { + // rValue = 0.0; // do not change caller's value! + rErr = 0; + return FALSE; // Ende und Aus + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + } + + while ( (nColRow < pCol->nCount) && (pCol->pItems[nColRow].nRow < nRow) ) + nColRow++; + + if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= aParam.nRow2 ) + { + nRow = pCol->pItems[nColRow].nRow; + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + if ( (pDoc->pTab[nTab])->ValidQuery( nRow, aParam, NULL, + (nCol == static_cast<SCCOL>(nFirstQueryField) ? pCell : NULL) ) ) + { + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + { + rValue = ((ScValueCell*)pCell)->GetValue(); + if ( bCalcAsShown ) + { + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); + rValue = pDoc->RoundValueAsShown( rValue, nNumFormat ); + } + nNumFmtType = NUMBERFORMAT_NUMBER; + nNumFmtIndex = 0; + rErr = 0; + return TRUE; // gefunden + } +// break; + case CELLTYPE_FORMULA: + { + if (((ScFormulaCell*)pCell)->IsValue()) + { + rValue = ((ScFormulaCell*)pCell)->GetValue(); + pDoc->GetNumberFormatInfo( nNumFmtType, + nNumFmtIndex, ScAddress( nCol, nRow, nTab ), + pCell ); + rErr = ((ScFormulaCell*)pCell)->GetErrCode(); + return TRUE; // gefunden + } + else + nRow++; + } + break; + default: + nRow++; + break; + } + } + else + nRow++; + } + else + nRow = aParam.nRow2 + 1; // Naechste Spalte + } +// return FALSE; +} + +BOOL ScQueryValueIterator::GetFirst(double& rValue, USHORT& rErr) +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + return GetThis(rValue, rErr); +} + +BOOL ScQueryValueIterator::GetNext(double& rValue, USHORT& rErr) +{ + ++nRow; + return GetThis(rValue, rErr); +} + +//------------------------------------------------------------------------------- + +ScCellIterator::ScCellIterator( ScDocument* pDocument, + SCCOL nSCol, SCROW nSRow, SCTAB nSTab, + SCCOL nECol, SCROW nERow, SCTAB nETab, BOOL bSTotal ) : + pDoc( pDocument ), + nStartCol( nSCol), + nStartRow( nSRow), + nStartTab( nSTab ), + nEndCol( nECol ), + nEndRow( nERow), + nEndTab( nETab ), + bSubTotal(bSTotal) + +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + while (nEndTab>0 && !pDoc->pTab[nEndTab]) + --nEndTab; // nur benutzte Tabellen + if (nStartTab>nEndTab) + nStartTab = nEndTab; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + nColRow = 0; // wird bei GetFirst initialisiert + + if (!pDoc->pTab[nTab]) + { + DBG_ERROR("Tabelle nicht gefunden"); + nStartCol = nCol = MAXCOL+1; + nStartRow = nRow = MAXROW+1; + nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst + } +} + +ScCellIterator::ScCellIterator + ( ScDocument* pDocument, const ScRange& rRange, BOOL bSTotal ) : + pDoc( pDocument ), + nStartCol( rRange.aStart.Col() ), + nStartRow( rRange.aStart.Row() ), + nStartTab( rRange.aStart.Tab() ), + nEndCol( rRange.aEnd.Col() ), + nEndRow( rRange.aEnd.Row() ), + nEndTab( rRange.aEnd.Tab() ), + bSubTotal(bSTotal) + +{ + PutInOrder( nStartCol, nEndCol); + PutInOrder( nStartRow, nEndRow); + PutInOrder( nStartTab, nEndTab ); + + if (!ValidCol(nStartCol)) nStartCol = MAXCOL; + if (!ValidCol(nEndCol)) nEndCol = MAXCOL; + if (!ValidRow(nStartRow)) nStartRow = MAXROW; + if (!ValidRow(nEndRow)) nEndRow = MAXROW; + if (!ValidTab(nStartTab)) nStartTab = MAXTAB; + if (!ValidTab(nEndTab)) nEndTab = MAXTAB; + + while (nEndTab>0 && !pDoc->pTab[nEndTab]) + --nEndTab; // nur benutzte Tabellen + if (nStartTab>nEndTab) + nStartTab = nEndTab; + + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; + nColRow = 0; // wird bei GetFirst initialisiert + + if (!pDoc->pTab[nTab]) + { + DBG_ERROR("Tabelle nicht gefunden"); + nStartCol = nCol = MAXCOL+1; + nStartRow = nRow = MAXROW+1; + nStartTab = nTab = MAXTAB+1; // -> Abbruch bei GetFirst + } +} + +ScBaseCell* ScCellIterator::GetThis() +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + for ( ;; ) + { + if ( nRow > nEndRow ) + { + nRow = nStartRow; + do + { + nCol++; + if ( nCol > nEndCol ) + { + nCol = nStartCol; + nTab++; + if ( nTab > nEndTab ) + return NULL; // Ende und Aus + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + } + + while ( (nColRow < pCol->nCount) && (pCol->pItems[nColRow].nRow < nRow) ) + nColRow++; + + if ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow <= nEndRow ) + { + nRow = pCol->pItems[nColRow].nRow; + if ( !bSubTotal || !pDoc->pTab[nTab]->IsFiltered( nRow ) ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + + if ( bSubTotal && pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->IsSubTotal() ) + nRow++; // Sub-Total-Zeilen nicht + else + return pCell; // gefunden + } + else + nRow++; + } + else + nRow = nEndRow + 1; // Naechste Spalte + } +} + +ScBaseCell* ScCellIterator::GetFirst() +{ + if ( !ValidTab(nTab) ) + return NULL; + nCol = nStartCol; + nRow = nStartRow; + nTab = nStartTab; +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + return GetThis(); +} + +ScBaseCell* ScCellIterator::GetNext() +{ + ++nRow; + return GetThis(); +} + +//------------------------------------------------------------------------------- + +ScQueryCellIterator::ScQueryCellIterator(ScDocument* pDocument, SCTAB nTable, + const ScQueryParam& rParam, BOOL bMod ) : + aParam (rParam), + pDoc( pDocument ), + nTab( nTable), + nStopOnMismatch( nStopOnMismatchDisabled ), + nTestEqualCondition( nTestEqualConditionDisabled ), + bAdvanceQuery( FALSE ), + bIgnoreMismatchOnLeadingStrings( FALSE ) +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + nColRow = 0; // wird bei GetFirst initialisiert + SCSIZE i; + if (bMod) // sonst schon eingetragen + { + for (i=0; (i<MAXQUERY) && (aParam.GetEntry(i).bDoQuery); i++) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pDoc->GetFormatTable()->IsNumberFormat(*rEntry.pStr, + nIndex, rEntry.nVal)); + } + } + nNumFormat = 0; // werden bei GetNumberFormat initialisiert + pAttrArray = 0; + nAttrEndRow = 0; +} + +ScBaseCell* ScQueryCellIterator::GetThis() +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + const ScQueryEntry& rEntry = aParam.GetEntry(0); + SCCOLROW nFirstQueryField = rEntry.nField; + bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && + !rEntry.bQueryByString; + bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && rEntry.bQueryByString && + ((aParam.bByRow && nRow == aParam.nRow1) || + (!aParam.bByRow && nCol == aParam.nCol1)); + for ( ;; ) + { + if ( nRow > aParam.nRow2 ) + { + nRow = aParam.nRow1; + if (aParam.bHasHeader && aParam.bByRow) + nRow++; + do + { + if ( ++nCol > aParam.nCol2 ) + return NULL; // Ende und Aus + if ( bAdvanceQuery ) + { + AdvanceQueryParamEntryField(); + nFirstQueryField = rEntry.nField; + } + pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + } while ( pCol->nCount == 0 ); + pCol->Search( nRow, nColRow ); + bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && rEntry.bQueryByString && + aParam.bByRow; + } + + while ( nColRow < pCol->nCount && pCol->pItems[nColRow].nRow < nRow ) + nColRow++; + + if ( nColRow < pCol->nCount && + (nRow = pCol->pItems[nColRow].nRow) <= aParam.nRow2 ) + { + ScBaseCell* pCell = pCol->pItems[nColRow].pCell; + if ( pCell->GetCellType() == CELLTYPE_NOTE ) + ++nRow; + else if (bAllStringIgnore && pCell->HasStringData()) + ++nRow; + else + { + BOOL bTestEqualCondition; + if ( (pDoc->pTab[nTab])->ValidQuery( nRow, aParam, NULL, + (nCol == static_cast<SCCOL>(nFirstQueryField) ? pCell : NULL), + (nTestEqualCondition ? &bTestEqualCondition : NULL) ) ) + { + if ( nTestEqualCondition && bTestEqualCondition ) + nTestEqualCondition |= nTestEqualConditionMatched; + return pCell; // found + } + else if ( nStopOnMismatch ) + { + // Yes, even a mismatch may have a fulfilled equal + // condition if regular expressions were involved and + // SC_LESS_EQUAL or SC_GREATER_EQUAL were queried. + if ( nTestEqualCondition && bTestEqualCondition ) + { + nTestEqualCondition |= nTestEqualConditionMatched; + nStopOnMismatch |= nStopOnMismatchOccured; + return NULL; + } + bool bStop; + if (bFirstStringIgnore) + { + if (pCell->HasStringData()) + { + ++nRow; + bStop = false; + } + else + bStop = true; + } + else + bStop = true; + if (bStop) + { + nStopOnMismatch |= nStopOnMismatchOccured; + return NULL; + } + } + else + nRow++; + } + } + else + nRow = aParam.nRow2 + 1; // Naechste Spalte + bFirstStringIgnore = false; + } +} + +ScBaseCell* ScQueryCellIterator::GetFirst() +{ + nCol = aParam.nCol1; + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; +// nColRow = 0; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + pCol->Search( nRow, nColRow ); + return GetThis(); +} + +ScBaseCell* ScQueryCellIterator::GetNext() +{ + ++nRow; + if ( nStopOnMismatch ) + nStopOnMismatch = nStopOnMismatchEnabled; + if ( nTestEqualCondition ) + nTestEqualCondition = nTestEqualConditionEnabled; + return GetThis(); +} + +ULONG ScQueryCellIterator::GetNumberFormat() +{ + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + lcl_IterGetNumberFormat( nNumFormat, pAttrArray, + nAttrEndRow, pCol->pAttrArray, nRow, pDoc ); + return nNumFormat; +} + +void ScQueryCellIterator::AdvanceQueryParamEntryField() +{ + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + if ( rEntry.nField < MAXCOL ) + rEntry.nField++; + else + { + DBG_ERRORFILE( "AdvanceQueryParamEntryField: ++rEntry.nField > MAXCOL" ); + } + } + else + break; // for + } +} + + +BOOL ScQueryCellIterator::FindEqualOrSortedLastInRange( SCCOL& nFoundCol, + SCROW& nFoundRow, BOOL bSearchForEqualAfterMismatch, + BOOL bIgnoreMismatchOnLeadingStringsP ) +{ + nFoundCol = MAXCOL+1; + nFoundRow = MAXROW+1; + SetStopOnMismatch( TRUE ); // assume sorted keys + SetTestEqualCondition( TRUE ); + bIgnoreMismatchOnLeadingStrings = bIgnoreMismatchOnLeadingStringsP; + bool bRegExp = aParam.bRegExp && aParam.GetEntry(0).bQueryByString; + bool bBinary = !bRegExp && aParam.bByRow && (aParam.GetEntry(0).eOp == + SC_LESS_EQUAL || aParam.GetEntry(0).eOp == SC_GREATER_EQUAL); + if (bBinary ? (BinarySearch() ? GetThis() : 0) : GetFirst()) + { + // First equal entry or last smaller than (greater than) entry. + SCSIZE nColRowSave; + ScBaseCell* pNext = 0; + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + } while ( !IsEqualConditionFulfilled() && (pNext = GetNext()) != NULL ); + // There may be no pNext but equal condition fulfilled if regular + // expressions are involved. Keep the found entry and proceed. + if (!pNext && !IsEqualConditionFulfilled()) + { + // Step back to last in range and adjust position markers for + // GetNumberFormat() or similar. + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + } + } + if ( IsEqualConditionFulfilled() ) + { + // Position on last equal entry. + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + switch ( rEntry.eOp ) + { + case SC_LESS_EQUAL : + case SC_GREATER_EQUAL : + rEntry.eOp = SC_EQUAL; + break; + default: + { + // added to avoid warnings + } + } + } + else + break; // for + } + SCSIZE nColRowSave; + bIgnoreMismatchOnLeadingStrings = FALSE; + SetTestEqualCondition( FALSE ); + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + } while (GetNext()); + // Step back conditions same as above + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + return TRUE; + } + if ( (bSearchForEqualAfterMismatch || aParam.bRegExp) && + StoppedOnMismatch() ) + { + // Assume found entry to be the last value less than respectively + // greater than the query. But keep on searching for an equal match. + SCSIZE nEntries = aParam.GetEntryCount(); + for ( SCSIZE j = 0; j < nEntries; j++ ) + { + ScQueryEntry& rEntry = aParam.GetEntry( j ); + if ( rEntry.bDoQuery ) + { + switch ( rEntry.eOp ) + { + case SC_LESS_EQUAL : + case SC_GREATER_EQUAL : + rEntry.eOp = SC_EQUAL; + break; + default: + { + // added to avoid warnings + } + } + } + else + break; // for + } + SetStopOnMismatch( FALSE ); + SetTestEqualCondition( FALSE ); + if (GetNext()) + { + // Last of a consecutive area, avoid searching the entire parameter + // range as it is a real performance bottleneck in case of regular + // expressions. + SCSIZE nColRowSave; + do + { + nFoundCol = GetCol(); + nFoundRow = GetRow(); + nColRowSave = nColRow; + SetStopOnMismatch( TRUE ); + } while (GetNext()); + nCol = nFoundCol; + nRow = nFoundRow; + nColRow = nColRowSave; + } + } + return (nFoundCol <= MAXCOL) && (nFoundRow <= MAXROW); +} + + +ScBaseCell* ScQueryCellIterator::BinarySearch() +{ + nCol = aParam.nCol1; + ScColumn* pCol = &(pDoc->pTab[nTab])->aCol[nCol]; + if (!pCol->nCount) + return 0; + + ScBaseCell* pCell; + SCSIZE nHi, nLo; + CollatorWrapper* pCollator = (aParam.bCaseSens ? ScGlobal::GetCaseCollator() : + ScGlobal::GetCollator()); + SvNumberFormatter& rFormatter = *(pDoc->GetFormatTable()); + const ScQueryEntry& rEntry = aParam.GetEntry(0); + bool bLessEqual = rEntry.eOp == SC_LESS_EQUAL; + bool bByString = rEntry.bQueryByString; + bool bAllStringIgnore = bIgnoreMismatchOnLeadingStrings && !bByString; + bool bFirstStringIgnore = bIgnoreMismatchOnLeadingStrings && + !aParam.bHasHeader && bByString; + + nRow = aParam.nRow1; + if (aParam.bHasHeader) + nRow++; + const ColEntry* pItems = pCol->pItems; + if (pCol->Search( nRow, nLo ) && bFirstStringIgnore && + pItems[nLo].pCell->HasStringData()) + { + String aCellStr; + ULONG nFormat = pCol->GetNumberFormat( pItems[nLo].nRow); + ScCellFormat::GetInputString( pItems[nLo].pCell, nFormat, aCellStr, + rFormatter); + sal_Int32 nTmp = pCollator->compareString( aCellStr, *rEntry.pStr); + if ((rEntry.eOp == SC_LESS_EQUAL && nTmp > 0) || + (rEntry.eOp == SC_GREATER_EQUAL && nTmp < 0) || + (rEntry.eOp == SC_EQUAL && nTmp != 0)) + ++nLo; + } + if (!pCol->Search( aParam.nRow2, nHi ) && nHi>0) + --nHi; + while (bAllStringIgnore && nLo <= nHi && nLo < pCol->nCount && + pItems[nLo].pCell->HasStringData()) + ++nLo; + + // Bookkeeping values for breaking up the binary search in case the data + // range isn't strictly sorted. + SCSIZE nLastInRange = nLo; + SCSIZE nFirstLastInRange = nLastInRange; + double fLastInRangeValue = bLessEqual ? + -(::std::numeric_limits<double>::max()) : + ::std::numeric_limits<double>::max(); + String aLastInRangeString; + if (!bLessEqual) + aLastInRangeString.Assign( sal_Unicode(0xFFFF)); + if (nLastInRange < pCol->nCount) + { + pCell = pItems[nLastInRange].pCell; + if (pCell->HasStringData()) + { + ULONG nFormat = pCol->GetNumberFormat( pItems[nLastInRange].nRow); + ScCellFormat::GetInputString( pCell, nFormat, aLastInRangeString, + rFormatter); + } + else + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + fLastInRangeValue = + static_cast<ScValueCell*>(pCell)->GetValue(); + break; + case CELLTYPE_FORMULA : + fLastInRangeValue = + static_cast<ScFormulaCell*>(pCell)->GetValue(); + break; + default: + { + // added to avoid warnings + } + } + } + } + + sal_Int32 nRes = 0; + bool bFound = false; + bool bDone = false; + while (nLo <= nHi && !bDone) + { + SCSIZE nMid = (nLo+nHi)/2; + SCSIZE i = nMid; + while (i <= nHi && pItems[i].pCell->GetCellType() == CELLTYPE_NOTE) + ++i; + if (i > nHi) + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + continue; // while + } + BOOL bStr = pItems[i].pCell->HasStringData(); + nRes = 0; + // compares are content<query:-1, content>query:1 + // Cell value comparison similar to ScTable::ValidQuery() + if (!bStr && !bByString) + { + double nCellVal; + pCell = pItems[i].pCell; + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + nCellVal = static_cast<ScValueCell*>(pCell)->GetValue(); + break; + case CELLTYPE_FORMULA : + nCellVal = static_cast<ScFormulaCell*>(pCell)->GetValue(); + break; + default: + nCellVal = 0.0; + } + if ((nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( + nCellVal, rEntry.nVal)) + { + nRes = -1; + if (bLessEqual) + { + if (fLastInRangeValue < nCellVal) + { + fLastInRangeValue = nCellVal; + nLastInRange = i; + } + else if (fLastInRangeValue > nCellVal) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + else if ((nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( + nCellVal, rEntry.nVal)) + { + nRes = 1; + if (!bLessEqual) + { + if (fLastInRangeValue > nCellVal) + { + fLastInRangeValue = nCellVal; + nLastInRange = i; + } + else if (fLastInRangeValue < nCellVal) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + } + else if (bStr && bByString) + { + String aCellStr; + ULONG nFormat = pCol->GetNumberFormat( pItems[i].nRow); + ScCellFormat::GetInputString( pItems[i].pCell, nFormat, aCellStr, + rFormatter); + nRes = pCollator->compareString( aCellStr, *rEntry.pStr); + if (nRes < 0 && bLessEqual) + { + sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, + aCellStr); + if (nTmp < 0) + { + aLastInRangeString = aCellStr; + nLastInRange = i; + } + else if (nTmp > 0) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + else if (nRes > 0 && !bLessEqual) + { + sal_Int32 nTmp = pCollator->compareString( aLastInRangeString, + aCellStr); + if (nTmp > 0) + { + aLastInRangeString = aCellStr; + nLastInRange = i; + } + else if (nTmp < 0) + { + // not strictly sorted, continue with GetThis() + nLastInRange = nFirstLastInRange; + bDone = true; + } + } + } + else if (!bStr && bByString) + { + nRes = -1; // numeric < string + if (bLessEqual) + nLastInRange = i; + } + else // if (bStr && !bByString) + { + nRes = 1; // string > numeric + if (!bLessEqual) + nLastInRange = i; + } + if (nRes < 0) + { + if (bLessEqual) + nLo = nMid + 1; + else // assumed to be SC_GREATER_EQUAL + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + } + } + else if (nRes > 0) + { + if (bLessEqual) + { + if (nMid > 0) + nHi = nMid - 1; + else + bDone = true; + } + else // assumed to be SC_GREATER_EQUAL + nLo = nMid + 1; + } + else + { + nLo = i; + bDone = bFound = true; + } + } + if (!bFound) + { + // If all hits didn't result in a moving limit there's something + // strange, e.g. data range not properly sorted, or only identical + // values encountered, which doesn't mean there aren't any others in + // between.. leave it to GetThis(). The condition for this would be + // if (nLastInRange == nFirstLastInRange) nLo = nFirstLastInRange; + // Else, in case no exact match was found, we step back for a + // subsequent GetThis() to find the last in range. Effectively this is + // --nLo with nLastInRange == nLo-1. Both conditions combined yield: + nLo = nLastInRange; + } + if (nLo < pCol->nCount && pCol->pItems[nLo].nRow <= aParam.nRow2) + { + nRow = pItems[nLo].nRow; + pCell = pItems[nLo].pCell; + nColRow = nLo; + } + else + { + nRow = aParam.nRow2 + 1; + pCell = 0; + nColRow = pCol->nCount - 1; + } + return pCell; +} + + +//------------------------------------------------------------------------------- + +ScHorizontalCellIterator::ScHorizontalCellIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + pDoc( pDocument ), + nTab( nTable ), + nStartCol( nCol1 ), + nEndCol( nCol2 ), + nEndRow( nRow2 ), + nCol( nCol1 ), + nRow( nRow1 ), + bMore( TRUE ) +{ + SCCOL i; + SCSIZE nIndex; + + pNextRows = new SCROW[ nCol2-nCol1+1 ]; + pNextIndices = new SCSIZE[ nCol2-nCol1+1 ]; + + for (i=nStartCol; i<=nEndCol; i++) + { + ScColumn* pCol = &pDoc->pTab[nTab]->aCol[i]; + + pCol->Search( nRow1, nIndex ); + if ( nIndex < pCol->nCount ) + { + pNextRows[i-nStartCol] = pCol->pItems[nIndex].nRow; + pNextIndices[i-nStartCol] = nIndex; + } + else + { + pNextRows[i-nStartCol] = MAXROWCOUNT; // nichts gefunden + pNextIndices[i-nStartCol] = MAXROWCOUNT; + } + } + + if (pNextRows[0] != nRow1) + Advance(); +} + +ScHorizontalCellIterator::~ScHorizontalCellIterator() +{ + delete [] pNextRows; + delete [] pNextIndices; +} + +ScBaseCell* ScHorizontalCellIterator::GetNext( SCCOL& rCol, SCROW& rRow ) +{ + if ( bMore ) + { + rCol = nCol; + rRow = nRow; + + ScColumn* pCol = &pDoc->pTab[nTab]->aCol[nCol]; + SCSIZE nIndex = pNextIndices[nCol-nStartCol]; + DBG_ASSERT( nIndex < pCol->nCount, "ScHorizontalCellIterator::GetNext: nIndex out of range" ); + ScBaseCell* pCell = pCol->pItems[nIndex].pCell; + if ( ++nIndex < pCol->nCount ) + { + pNextRows[nCol-nStartCol] = pCol->pItems[nIndex].nRow; + pNextIndices[nCol-nStartCol] = nIndex; + } + else + { + pNextRows[nCol-nStartCol] = MAXROWCOUNT; // nichts gefunden + pNextIndices[nCol-nStartCol] = MAXROWCOUNT; + } + + Advance(); + return pCell; + } + else + return NULL; +} + +BOOL ScHorizontalCellIterator::ReturnNext( SCCOL& rCol, SCROW& rRow ) +{ + rCol = nCol; + rRow = nRow; + return bMore; +} + +void ScHorizontalCellIterator::Advance() +{ + BOOL bFound = FALSE; + SCCOL i; + + for (i=nCol+1; i<=nEndCol && !bFound; i++) + if (pNextRows[i-nStartCol] == nRow) + { + nCol = i; + bFound = TRUE; + } + + if (!bFound) + { + SCROW nMinRow = MAXROW+1; + for (i=nStartCol; i<=nEndCol; i++) + if (pNextRows[i-nStartCol] < nMinRow) + { + nCol = i; + nMinRow = pNextRows[i-nStartCol]; + } + + if (nMinRow <= nEndRow) + { + nRow = nMinRow; + bFound = TRUE; + } + } + + if ( !bFound ) + bMore = FALSE; +} + +//------------------------------------------------------------------------------- + +ScHorizontalAttrIterator::ScHorizontalAttrIterator( ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + pDoc( pDocument ), + nTab( nTable ), + nStartCol( nCol1 ), + nStartRow( nRow1 ), + nEndCol( nCol2 ), + nEndRow( nRow2 ) +{ + DBG_ASSERT( pDoc->pTab[nTab], "Tabelle nicht da" ); + + SCCOL i; + + nRow = nStartRow; + nCol = nStartCol; + bRowEmpty = FALSE; + + pIndices = new SCSIZE[nEndCol-nStartCol+1]; + pNextEnd = new SCROW[nEndCol-nStartCol+1]; + ppPatterns = new const ScPatternAttr*[nEndCol-nStartCol+1]; + + SCROW nSkipTo = MAXROW; + BOOL bEmpty = TRUE; + for (i=nStartCol; i<=nEndCol; i++) + { + SCCOL nPos = i - nStartCol; + ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; + DBG_ASSERT( pArray, "pArray == 0" ); + + SCSIZE nIndex; + pArray->Search( nStartRow, nIndex ); + + const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; + SCROW nThisEnd = pArray->pData[nIndex].nRow; + if ( IsDefaultItem( pPattern ) ) + { + pPattern = NULL; + if ( nThisEnd < nSkipTo ) + nSkipTo = nThisEnd; // nSkipTo kann gleich hier gesetzt werden + } + else + bEmpty = FALSE; // Attribute gefunden + + pIndices[nPos] = nIndex; + pNextEnd[nPos] = nThisEnd; + ppPatterns[nPos] = pPattern; + } + + if (bEmpty) + nRow = nSkipTo; // bis zum naechsten Bereichsende ueberspringen + bRowEmpty = bEmpty; +} + +ScHorizontalAttrIterator::~ScHorizontalAttrIterator() +{ + delete[] (ScPatternAttr**)ppPatterns; + delete[] pNextEnd; + delete[] pIndices; +} + +const ScPatternAttr* ScHorizontalAttrIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, SCROW& rRow ) +{ + for (;;) + { + if (!bRowEmpty) + { + // in dieser Zeile suchen + + while ( nCol <= nEndCol && !ppPatterns[nCol-nStartCol] ) + ++nCol; + + if ( nCol <= nEndCol ) + { + const ScPatternAttr* pPat = ppPatterns[nCol-nStartCol]; + rRow = nRow; + rCol1 = nCol; + while ( nCol < nEndCol && ppPatterns[nCol+1-nStartCol] == pPat ) + ++nCol; + rCol2 = nCol; + ++nCol; // hochzaehlen fuer naechsten Aufruf + return pPat; // gefunden + } + } + + // naechste Zeile + + ++nRow; + if ( nRow > nEndRow ) // schon am Ende? + return NULL; // nichts gefunden + + BOOL bEmpty = TRUE; + SCCOL i; + + for ( i = nStartCol; i <= nEndCol; i++) + { + SCCOL nPos = i-nStartCol; + if ( pNextEnd[nPos] < nRow ) + { + ScAttrArray* pArray = pDoc->pTab[nTab]->aCol[i].pAttrArray; + + SCSIZE nIndex = ++pIndices[nPos]; + if ( nIndex < pArray->nCount ) + { + const ScPatternAttr* pPattern = pArray->pData[nIndex].pPattern; + SCROW nThisEnd = pArray->pData[nIndex].nRow; + if ( IsDefaultItem( pPattern ) ) + pPattern = NULL; + else + bEmpty = FALSE; // Attribute gefunden + + pNextEnd[nPos] = nThisEnd; + ppPatterns[nPos] = pPattern; + + DBG_ASSERT( pNextEnd[nPos] >= nRow, "Reihenfolge durcheinander" ); + } + else + { + DBG_ERROR("AttrArray reicht nicht bis MAXROW"); + pNextEnd[nPos] = MAXROW; + ppPatterns[nPos] = NULL; + } + } + else if ( ppPatterns[nPos] ) + bEmpty = FALSE; // Bereich noch nicht zuende + } + + if (bEmpty) + { + SCCOL nCount = nEndCol-nStartCol+1; + SCROW nSkipTo = pNextEnd[0]; // naechstes Bereichsende suchen + for (i=1; i<nCount; i++) + if ( pNextEnd[i] < nSkipTo ) + nSkipTo = pNextEnd[i]; + nRow = nSkipTo; // leere Zeilen ueberspringen + } + bRowEmpty = bEmpty; + nCol = nStartCol; // wieder links anfangen + } + +// return NULL; +} + +//------------------------------------------------------------------------------- + +inline BOOL IsGreater( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) +{ + return ( nRow1 > nRow2 ) || ( nRow1 == nRow2 && nCol1 > nCol2 ); +} + +ScUsedAreaIterator::ScUsedAreaIterator( ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) : + aCellIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), + aAttrIter( pDocument, nTable, nCol1, nRow1, nCol2, nRow2 ), + nNextCol( nCol1 ), + nNextRow( nRow1 ) +{ + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); +} + +ScUsedAreaIterator::~ScUsedAreaIterator() +{ +} + +BOOL ScUsedAreaIterator::GetNext() +{ + // Iteratoren weiterzaehlen + + if ( pCell && IsGreater( nNextCol, nNextRow, nCellCol, nCellRow ) ) + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + + while ( pCell && pCell->IsBlank() ) + pCell = aCellIter.GetNext( nCellCol, nCellRow ); + + if ( pPattern && IsGreater( nNextCol, nNextRow, nAttrCol2, nAttrRow ) ) + pPattern = aAttrIter.GetNext( nAttrCol1, nAttrCol2, nAttrRow ); + + if ( pPattern && nAttrRow == nNextRow && nAttrCol1 < nNextCol ) + nAttrCol1 = nNextCol; + + // naechsten Abschnitt heraussuchen + + BOOL bFound = TRUE; + BOOL bUseCell = FALSE; + + if ( pCell && pPattern ) + { + if ( IsGreater( nCellCol, nCellRow, nAttrCol1, nAttrRow ) ) // vorne nur Attribute ? + { + pFoundCell = NULL; + pFoundPattern = pPattern; + nFoundRow = nAttrRow; + nFoundStartCol = nAttrCol1; + if ( nCellRow == nAttrRow && nCellCol <= nAttrCol2 ) // auch Zelle im Bereich ? + nFoundEndCol = nCellCol - 1; // nur bis vor der Zelle + else + nFoundEndCol = nAttrCol2; // alles + } + else + { + bUseCell = TRUE; + if ( nAttrRow == nCellRow && nAttrCol1 == nCellCol ) // Attribute auf der Zelle ? + pFoundPattern = pPattern; + else + pFoundPattern = NULL; + } + } + else if ( pCell ) // nur Zelle -> direkt uebernehmen + { + pFoundPattern = NULL; + bUseCell = TRUE; // Position von Zelle + } + else if ( pPattern ) // nur Attribute -> direkt uebernehmen + { + pFoundCell = NULL; + pFoundPattern = pPattern; + nFoundRow = nAttrRow; + nFoundStartCol = nAttrCol1; + nFoundEndCol = nAttrCol2; + } + else // gar nichts + bFound = FALSE; + + if ( bUseCell ) // Position von Zelle + { + pFoundCell = pCell; + nFoundRow = nCellRow; + nFoundStartCol = nFoundEndCol = nCellCol; + } + + if (bFound) + { + nNextRow = nFoundRow; + nNextCol = nFoundEndCol + 1; + } + + return bFound; +} + +//------------------------------------------------------------------------------- + +ScDocAttrIterator::ScDocAttrIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2) : + pDoc( pDocument ), + nTab( nTable ), + nEndCol( nCol2 ), + nStartRow( nRow1 ), + nEndRow( nRow2 ), + nCol( nCol1 ) +{ + if ( ValidTab(nTab) && pDoc->pTab[nTab] ) + pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); + else + pColIter = NULL; +} + +ScDocAttrIterator::~ScDocAttrIterator() +{ + delete pColIter; +} + +const ScPatternAttr* ScDocAttrIterator::GetNext( SCCOL& rCol, SCROW& rRow1, SCROW& rRow2 ) +{ + while ( pColIter ) + { + const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); + if ( pPattern ) + { + rCol = nCol; + return pPattern; + } + + delete pColIter; + ++nCol; + if ( nCol <= nEndCol ) + pColIter = pDoc->pTab[nTab]->aCol[nCol].CreateAttrIterator( nStartRow, nEndRow ); + else + pColIter = NULL; + } + return NULL; // is nix mehr +} + +//------------------------------------------------------------------------------- + +ScAttrRectIterator::ScAttrRectIterator(ScDocument* pDocument, SCTAB nTable, + SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2) : + pDoc( pDocument ), + nTab( nTable ), + nEndCol( nCol2 ), + nStartRow( nRow1 ), + nEndRow( nRow2 ), + nIterStartCol( nCol1 ), + nIterEndCol( nCol1 ) +{ + if ( ValidTab(nTab) && pDoc->pTab[nTab] ) + { + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); + while ( nIterEndCol < nEndCol && + pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( + pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) + ++nIterEndCol; + } + else + pColIter = NULL; +} + +ScAttrRectIterator::~ScAttrRectIterator() +{ + delete pColIter; +} + +void ScAttrRectIterator::DataChanged() +{ + if (pColIter) + { + SCROW nNextRow = pColIter->GetNextRow(); + delete pColIter; + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nNextRow, nEndRow ); + } +} + +const ScPatternAttr* ScAttrRectIterator::GetNext( SCCOL& rCol1, SCCOL& rCol2, + SCROW& rRow1, SCROW& rRow2 ) +{ + while ( pColIter ) + { + const ScPatternAttr* pPattern = pColIter->Next( rRow1, rRow2 ); + if ( pPattern ) + { + rCol1 = nIterStartCol; + rCol2 = nIterEndCol; + return pPattern; + } + + delete pColIter; + nIterStartCol = nIterEndCol+1; + if ( nIterStartCol <= nEndCol ) + { + nIterEndCol = nIterStartCol; + pColIter = pDoc->pTab[nTab]->aCol[nIterStartCol].CreateAttrIterator( nStartRow, nEndRow ); + while ( nIterEndCol < nEndCol && + pDoc->pTab[nTab]->aCol[nIterEndCol].IsAllAttrEqual( + pDoc->pTab[nTab]->aCol[nIterEndCol+1], nStartRow, nEndRow ) ) + ++nIterEndCol; + } + else + pColIter = NULL; + } + return NULL; // is nix mehr +} + diff --git a/sc/source/core/data/docpool.cxx b/sc/source/core/data/docpool.cxx new file mode 100644 index 000000000000..fce83f25cd93 --- /dev/null +++ b/sc/source/core/data/docpool.cxx @@ -0,0 +1,1042 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: docpool.cxx,v $ + * $Revision: 1.25.144.1 $ + * + * 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 "scitems.hxx" +#include <tools/shl.hxx> +#include <vcl/outdev.hxx> +#include <svtools/aeitem.hxx> +#include <svtools/itemiter.hxx> +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/bolnitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/charreliefitem.hxx> +#include <svx/cntritem.hxx> +#include <svx/colritem.hxx> +#include <svx/crsditem.hxx> +#include <svx/dialmgr.hxx> +#include <svx/emphitem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/fontitem.hxx> +#include <svx/forbiddenruleitem.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/hngpnctitem.hxx> +#include <svx/itemtype.hxx> +#include <svx/langitem.hxx> +#include <svx/lrspitem.hxx> +#include <svx/pageitem.hxx> +#include <svx/pbinitem.hxx> +#include <svx/postitem.hxx> +#include <svx/rotmodit.hxx> +#include <svx/scriptspaceitem.hxx> +#include <svx/shaditem.hxx> +#include <svx/shdditem.hxx> +#include <svx/sizeitem.hxx> +#include <svx/svxitems.hrc> +#include <svx/udlnitem.hxx> +#include <svx/ulspitem.hxx> +#include <svx/wghtitem.hxx> +#include <svx/wrlmitem.hxx> +#include <svx/xmlcnitm.hxx> + +#include "docpool.hxx" +#include "global.hxx" +#include "attrib.hxx" +#include "patattr.hxx" +#include "globstr.hrc" +#include "sc.hrc" // Slot-IDs + + +#define SC_MAX_POOLREF (SFX_ITEMS_OLD_MAXREF - 39) +#define SC_SAFE_POOLREF (SC_MAX_POOLREF + 20) + +// STATIC DATA ----------------------------------------------------------- + +USHORT* ScDocumentPool::pVersionMap1 = 0; +USHORT* ScDocumentPool::pVersionMap2 = 0; +USHORT* ScDocumentPool::pVersionMap3 = 0; +USHORT* ScDocumentPool::pVersionMap4 = 0; +USHORT* ScDocumentPool::pVersionMap5 = 0; +USHORT* ScDocumentPool::pVersionMap6 = 0; +USHORT* ScDocumentPool::pVersionMap7 = 0; +USHORT* ScDocumentPool::pVersionMap8 = 0; +USHORT* ScDocumentPool::pVersionMap9 = 0; +USHORT* ScDocumentPool::pVersionMap10 = 0; +USHORT* ScDocumentPool::pVersionMap11 = 0; + +// ATTR_FONT_TWOLINES (not used) was changed to ATTR_USERDEF (not saved in binary format) in 641c + +static SfxItemInfo __READONLY_DATA aItemInfos[] = +{ + { SID_ATTR_CHAR_FONT, SFX_ITEM_POOLABLE }, // ATTR_FONT + { SID_ATTR_CHAR_FONTHEIGHT, SFX_ITEM_POOLABLE }, // ATTR_FONT_HEIGHT + { SID_ATTR_CHAR_WEIGHT, SFX_ITEM_POOLABLE }, // ATTR_FONT_WEIGHT + { SID_ATTR_CHAR_POSTURE, SFX_ITEM_POOLABLE }, // ATTR_FONT_POSTURE + { SID_ATTR_CHAR_UNDERLINE, SFX_ITEM_POOLABLE }, // ATTR_FONT_UNDERLINE + { SID_ATTR_CHAR_OVERLINE, SFX_ITEM_POOLABLE }, // ATTR_FONT_OVERLINE + { SID_ATTR_CHAR_STRIKEOUT, SFX_ITEM_POOLABLE }, // ATTR_FONT_CROSSEDOUT + { SID_ATTR_CHAR_CONTOUR, SFX_ITEM_POOLABLE }, // ATTR_FONT_CONTOUR + { SID_ATTR_CHAR_SHADOWED, SFX_ITEM_POOLABLE }, // ATTR_FONT_SHADOWED + { SID_ATTR_CHAR_COLOR, SFX_ITEM_POOLABLE }, // ATTR_FONT_COLOR + { SID_ATTR_CHAR_LANGUAGE, SFX_ITEM_POOLABLE }, // ATTR_FONT_LANGUAGE + { SID_ATTR_CHAR_CJK_FONT, SFX_ITEM_POOLABLE }, // ATTR_CJK_FONT from 614 + { SID_ATTR_CHAR_CJK_FONTHEIGHT, SFX_ITEM_POOLABLE }, // ATTR_CJK_FONT_HEIGHT from 614 + { SID_ATTR_CHAR_CJK_WEIGHT, SFX_ITEM_POOLABLE }, // ATTR_CJK_FONT_WEIGHT from 614 + { SID_ATTR_CHAR_CJK_POSTURE, SFX_ITEM_POOLABLE }, // ATTR_CJK_FONT_POSTURE from 614 + { SID_ATTR_CHAR_CJK_LANGUAGE, SFX_ITEM_POOLABLE }, // ATTR_CJK_FONT_LANGUAGE from 614 + { SID_ATTR_CHAR_CTL_FONT, SFX_ITEM_POOLABLE }, // ATTR_CTL_FONT from 614 + { SID_ATTR_CHAR_CTL_FONTHEIGHT, SFX_ITEM_POOLABLE }, // ATTR_CTL_FONT_HEIGHT from 614 + { SID_ATTR_CHAR_CTL_WEIGHT, SFX_ITEM_POOLABLE }, // ATTR_CTL_FONT_WEIGHT from 614 + { SID_ATTR_CHAR_CTL_POSTURE, SFX_ITEM_POOLABLE }, // ATTR_CTL_FONT_POSTURE from 614 + { SID_ATTR_CHAR_CTL_LANGUAGE, SFX_ITEM_POOLABLE }, // ATTR_CTL_FONT_LANGUAGE from 614 + { SID_ATTR_CHAR_EMPHASISMARK, SFX_ITEM_POOLABLE }, // ATTR_FONT_EMPHASISMARK from 614 + { 0, SFX_ITEM_POOLABLE }, // ATTR_USERDEF from 614 / 641c + { SID_ATTR_CHAR_WORDLINEMODE, SFX_ITEM_POOLABLE }, // ATTR_FONT_WORDLINE from 632b + { SID_ATTR_CHAR_RELIEF, SFX_ITEM_POOLABLE }, // ATTR_FONT_RELIEF from 632b + { SID_ATTR_ALIGN_HYPHENATION, SFX_ITEM_POOLABLE }, // ATTR_HYPHENATE from 632b + { 0, SFX_ITEM_POOLABLE }, // ATTR_SCRIPTSPACE from 614d + { 0, SFX_ITEM_POOLABLE }, // ATTR_HANGPUNCTUATION from 614d + { SID_ATTR_PARA_FORBIDDEN_RULES,SFX_ITEM_POOLABLE }, // ATTR_FORBIDDEN_RULES from 614d + { SID_ATTR_ALIGN_HOR_JUSTIFY, SFX_ITEM_POOLABLE }, // ATTR_HOR_JUSTIFY + { SID_ATTR_ALIGN_INDENT, SFX_ITEM_POOLABLE }, // ATTR_INDENT ab 350 + { SID_ATTR_ALIGN_VER_JUSTIFY, SFX_ITEM_POOLABLE }, // ATTR_VER_JUSTIFY + { SID_ATTR_ALIGN_STACKED, SFX_ITEM_POOLABLE }, // ATTR_STACKED from 680/dr14 (replaces ATTR_ORIENTATION) + { SID_ATTR_ALIGN_DEGREES, SFX_ITEM_POOLABLE }, // ATTR_ROTATE_VALUE ab 367 + { SID_ATTR_ALIGN_LOCKPOS, SFX_ITEM_POOLABLE }, // ATTR_ROTATE_MODE ab 367 + { SID_ATTR_ALIGN_ASIANVERTICAL, SFX_ITEM_POOLABLE }, // ATTR_VERTICAL_ASIAN from 642 + { SID_ATTR_FRAMEDIRECTION, SFX_ITEM_POOLABLE }, // ATTR_WRITINGDIR from 643 + { SID_ATTR_ALIGN_LINEBREAK, SFX_ITEM_POOLABLE }, // ATTR_LINEBREAK + { SID_ATTR_ALIGN_SHRINKTOFIT, SFX_ITEM_POOLABLE }, // ATTR_SHRINKTOFIT from 680/dr14 + { SID_ATTR_BORDER_DIAG_TLBR, SFX_ITEM_POOLABLE }, // ATTR_BORDER_TLBR from 680/dr14 + { SID_ATTR_BORDER_DIAG_BLTR, SFX_ITEM_POOLABLE }, // ATTR_BORDER_BLTR from 680/dr14 + { SID_ATTR_ALIGN_MARGIN, SFX_ITEM_POOLABLE }, // ATTR_MARGIN + { 0, SFX_ITEM_POOLABLE }, // ATTR_MERGE + { 0, SFX_ITEM_POOLABLE }, // ATTR_MERGE_FLAG + { SID_ATTR_NUMBERFORMAT_VALUE, SFX_ITEM_POOLABLE }, // ATTR_VALUE_FORMAT + { ATTR_LANGUAGE_FORMAT, SFX_ITEM_POOLABLE }, // ATTR_LANGUAGE_FORMAT ab 329, wird im Dialog mit SID_ATTR_NUMBERFORMAT_VALUE kombiniert + { SID_ATTR_BRUSH, SFX_ITEM_POOLABLE }, // ATTR_BACKGROUND + { SID_SCATTR_PROTECTION, SFX_ITEM_POOLABLE }, // ATTR_PROTECTION + { SID_ATTR_BORDER_OUTER, SFX_ITEM_POOLABLE }, // ATTR_BORDER + { SID_ATTR_BORDER_INNER, SFX_ITEM_POOLABLE }, // ATTR_BORDER_INNER + { SID_ATTR_BORDER_SHADOW, SFX_ITEM_POOLABLE }, // ATTR_SHADOW + { 0, SFX_ITEM_POOLABLE }, // ATTR_VALIDDATA + { 0, SFX_ITEM_POOLABLE }, // ATTR_CONDITIONAL + { 0, SFX_ITEM_POOLABLE }, // ATTR_PATTERN + { SID_ATTR_LRSPACE, SFX_ITEM_POOLABLE }, // ATTR_LRSPACE + { SID_ATTR_ULSPACE, SFX_ITEM_POOLABLE }, // ATTR_ULSPACE + { SID_ATTR_PAGE, SFX_ITEM_POOLABLE }, // ATTR_PAGE + { 0, SFX_ITEM_POOLABLE }, // ATTR_PAGE_PAPERTRAY, seit 303 nur noch dummy + { SID_ATTR_PAGE_PAPERBIN, SFX_ITEM_POOLABLE }, // ATTR_PAGE_PAPERBIN + { SID_ATTR_PAGE_SIZE, SFX_ITEM_POOLABLE }, // ATTR_PAGE_SIZE + { SID_ATTR_PAGE_MAXSIZE, SFX_ITEM_POOLABLE }, // ATTR_PAGE_MAXSIZE + { SID_ATTR_PAGE_EXT1, SFX_ITEM_POOLABLE }, // ATTR_PAGE_HORCENTER + { SID_ATTR_PAGE_EXT2, SFX_ITEM_POOLABLE }, // ATTR_PAGE_VERCENTER + { SID_ATTR_PAGE_ON, SFX_ITEM_POOLABLE }, // ATTR_PAGE_ON + { SID_ATTR_PAGE_DYNAMIC, SFX_ITEM_POOLABLE }, // ATTR_PAGE_DYNAMIC + { SID_ATTR_PAGE_SHARED, SFX_ITEM_POOLABLE }, // ATTR_PAGE_SHARED + { SID_SCATTR_PAGE_NOTES, SFX_ITEM_POOLABLE }, // ATTR_PAGE_NOTES + { SID_SCATTR_PAGE_GRID, SFX_ITEM_POOLABLE }, // ATTR_PAGE_GRID + { SID_SCATTR_PAGE_HEADERS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_HEADERS + { SID_SCATTR_PAGE_CHARTS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_CHARTS + { SID_SCATTR_PAGE_OBJECTS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_OBJECTS + { SID_SCATTR_PAGE_DRAWINGS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_DRAWINGS + { SID_SCATTR_PAGE_TOPDOWN, SFX_ITEM_POOLABLE }, // ATTR_PAGE_TOPDOWN + { SID_SCATTR_PAGE_SCALE, SFX_ITEM_POOLABLE }, // ATTR_PAGE_SCALE + { SID_SCATTR_PAGE_SCALETOPAGES, SFX_ITEM_POOLABLE }, // ATTR_PAGE_SCALETOPAGES + { SID_SCATTR_PAGE_FIRSTPAGENO, SFX_ITEM_POOLABLE }, // ATTR_PAGE_FIRSTPAGENO + { SID_SCATTR_PAGE_PRINTAREA, SFX_ITEM_POOLABLE }, // ATTR_PAGE_PRINTAREA + { SID_SCATTR_PAGE_REPEATROW, SFX_ITEM_POOLABLE }, // ATTR_PAGE_REPEATROW + { SID_SCATTR_PAGE_REPEATCOL, SFX_ITEM_POOLABLE }, // ATTR_PAGE_REPEATCOL + { SID_SCATTR_PAGE_PRINTTABLES, SFX_ITEM_POOLABLE }, // ATTR_PAGE_PRINTTABLES + { SID_SCATTR_PAGE_HEADERLEFT, SFX_ITEM_POOLABLE }, // ATTR_PAGE_HEADERLEFT + { SID_SCATTR_PAGE_FOOTERLEFT, SFX_ITEM_POOLABLE }, // ATTR_PAGE_FOOTERLEFT + { SID_SCATTR_PAGE_HEADERRIGHT, SFX_ITEM_POOLABLE }, // ATTR_PAGE_HEADERRIGHT + { SID_SCATTR_PAGE_FOOTERRIGHT, SFX_ITEM_POOLABLE }, // ATTR_PAGE_FOOTERRIGHT + { SID_ATTR_PAGE_HEADERSET, SFX_ITEM_POOLABLE }, // ATTR_PAGE_HEADERSET + { SID_ATTR_PAGE_FOOTERSET, SFX_ITEM_POOLABLE }, // ATTR_PAGE_FOOTERSET + { SID_SCATTR_PAGE_FORMULAS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_FORMULAS + { SID_SCATTR_PAGE_NULLVALS, SFX_ITEM_POOLABLE }, // ATTR_PAGE_NULLVALS + { SID_SCATTR_PAGE_SCALETO, SFX_ITEM_POOLABLE } // ATTR_PAGE_SCALETO +}; + +// ----------------------------------------------------------------------- + +ScDocumentPool::ScDocumentPool( SfxItemPool* pSecPool, BOOL bLoadRefCounts ) + + : SfxItemPool ( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("ScDocumentPool")), + ATTR_STARTINDEX, ATTR_ENDINDEX, + aItemInfos, NULL, bLoadRefCounts ), + pSecondary ( pSecPool ) +{ + // latin font from GetDefaultFonts is not used, DEFAULTFONT_LATIN_SPREADSHEET instead + Font aStdFont = OutputDevice::GetDefaultFont( DEFAULTFONT_LATIN_SPREADSHEET, LANGUAGE_ENGLISH_US, + DEFAULTFONT_FLAGS_ONLYONE ); + SvxFontItem* pStdFont = new SvxFontItem( aStdFont.GetFamily(), + aStdFont.GetName(), aStdFont.GetStyleName(), + aStdFont.GetPitch(), aStdFont.GetCharSet(), + ATTR_FONT ); + + SvxFontItem* pCjkFont = new SvxFontItem( ATTR_CJK_FONT ); + SvxFontItem* pCtlFont = new SvxFontItem( ATTR_CTL_FONT ); + SvxFontItem aDummy( ATTR_FONT ); + GetDefaultFonts( aDummy, *pCjkFont, *pCtlFont ); + + SvxBoxInfoItem* pGlobalBorderInnerAttr = new SvxBoxInfoItem( ATTR_BORDER_INNER ); + SfxItemSet* pSet = new SfxItemSet( *this, ATTR_PATTERN_START, ATTR_PATTERN_END ); + SfxItemSet aSetItemItemSet( *this, + ATTR_BACKGROUND, ATTR_BACKGROUND, + ATTR_BORDER, ATTR_SHADOW, + ATTR_LRSPACE, ATTR_ULSPACE, + ATTR_PAGE_SIZE, ATTR_PAGE_SIZE, + ATTR_PAGE_ON, ATTR_PAGE_SHARED, + 0 ); + + pGlobalBorderInnerAttr->SetLine(NULL, BOXINFO_LINE_HORI); + pGlobalBorderInnerAttr->SetLine(NULL, BOXINFO_LINE_VERT); + pGlobalBorderInnerAttr->SetTable(TRUE); + pGlobalBorderInnerAttr->SetDist(TRUE); + pGlobalBorderInnerAttr->SetMinDist(FALSE); + + ppPoolDefaults = new SfxPoolItem*[ATTR_ENDINDEX-ATTR_STARTINDEX+1]; + + ppPoolDefaults[ ATTR_FONT - ATTR_STARTINDEX ] = pStdFont; + ppPoolDefaults[ ATTR_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_FONT_HEIGHT ); // 10 pt; + ppPoolDefaults[ ATTR_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_FONT_WEIGHT ); + ppPoolDefaults[ ATTR_FONT_POSTURE - ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_FONT_POSTURE ); + ppPoolDefaults[ ATTR_FONT_UNDERLINE - ATTR_STARTINDEX ] = new SvxUnderlineItem( UNDERLINE_NONE, ATTR_FONT_UNDERLINE ); + ppPoolDefaults[ ATTR_FONT_OVERLINE - ATTR_STARTINDEX ] = new SvxOverlineItem( UNDERLINE_NONE, ATTR_FONT_OVERLINE ); + ppPoolDefaults[ ATTR_FONT_CROSSEDOUT - ATTR_STARTINDEX ] = new SvxCrossedOutItem( STRIKEOUT_NONE, ATTR_FONT_CROSSEDOUT ); + ppPoolDefaults[ ATTR_FONT_CONTOUR - ATTR_STARTINDEX ] = new SvxContourItem( sal_False, ATTR_FONT_CONTOUR ); + ppPoolDefaults[ ATTR_FONT_SHADOWED - ATTR_STARTINDEX ] = new SvxShadowedItem( sal_False, ATTR_FONT_SHADOWED ); + ppPoolDefaults[ ATTR_FONT_COLOR - ATTR_STARTINDEX ] = new SvxColorItem( Color(COL_AUTO), ATTR_FONT_COLOR ); + ppPoolDefaults[ ATTR_FONT_LANGUAGE - ATTR_STARTINDEX ] = new SvxLanguageItem( LanguageType(LANGUAGE_DONTKNOW), ATTR_FONT_LANGUAGE ); + ppPoolDefaults[ ATTR_CJK_FONT - ATTR_STARTINDEX ] = pCjkFont; + ppPoolDefaults[ ATTR_CJK_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_CJK_FONT_HEIGHT ); + ppPoolDefaults[ ATTR_CJK_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_CJK_FONT_WEIGHT ); + ppPoolDefaults[ ATTR_CJK_FONT_POSTURE- ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_CJK_FONT_POSTURE ); + ppPoolDefaults[ ATTR_CJK_FONT_LANGUAGE-ATTR_STARTINDEX ] = new SvxLanguageItem( LanguageType(LANGUAGE_DONTKNOW), + ATTR_CJK_FONT_LANGUAGE ); + ppPoolDefaults[ ATTR_CTL_FONT - ATTR_STARTINDEX ] = pCtlFont; + ppPoolDefaults[ ATTR_CTL_FONT_HEIGHT - ATTR_STARTINDEX ] = new SvxFontHeightItem( 200, 100, ATTR_CTL_FONT_HEIGHT ); + ppPoolDefaults[ ATTR_CTL_FONT_WEIGHT - ATTR_STARTINDEX ] = new SvxWeightItem( WEIGHT_NORMAL, ATTR_CTL_FONT_WEIGHT ); + ppPoolDefaults[ ATTR_CTL_FONT_POSTURE- ATTR_STARTINDEX ] = new SvxPostureItem( ITALIC_NONE, ATTR_CTL_FONT_POSTURE ); + ppPoolDefaults[ ATTR_CTL_FONT_LANGUAGE-ATTR_STARTINDEX ] = new SvxLanguageItem( LanguageType(LANGUAGE_DONTKNOW), + ATTR_CTL_FONT_LANGUAGE ); + ppPoolDefaults[ ATTR_FONT_EMPHASISMARK-ATTR_STARTINDEX ] = new SvxEmphasisMarkItem( EMPHASISMARK_NONE, ATTR_FONT_EMPHASISMARK ); + ppPoolDefaults[ ATTR_USERDEF - ATTR_STARTINDEX ] = new SvXMLAttrContainerItem( ATTR_USERDEF ); + ppPoolDefaults[ ATTR_FONT_WORDLINE - ATTR_STARTINDEX ] = new SvxWordLineModeItem(sal_False, ATTR_FONT_WORDLINE ); + ppPoolDefaults[ ATTR_FONT_RELIEF - ATTR_STARTINDEX ] = new SvxCharReliefItem( RELIEF_NONE, ATTR_FONT_RELIEF ); + ppPoolDefaults[ ATTR_HYPHENATE - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_HYPHENATE ); + ppPoolDefaults[ ATTR_SCRIPTSPACE - ATTR_STARTINDEX ] = new SvxScriptSpaceItem( sal_False, ATTR_SCRIPTSPACE); + ppPoolDefaults[ ATTR_HANGPUNCTUATION - ATTR_STARTINDEX ] = new SvxHangingPunctuationItem( sal_False, ATTR_HANGPUNCTUATION); + ppPoolDefaults[ ATTR_FORBIDDEN_RULES - ATTR_STARTINDEX ] = new SvxForbiddenRuleItem( sal_False, ATTR_FORBIDDEN_RULES); + ppPoolDefaults[ ATTR_HOR_JUSTIFY - ATTR_STARTINDEX ] = new SvxHorJustifyItem( SVX_HOR_JUSTIFY_STANDARD, ATTR_HOR_JUSTIFY); + ppPoolDefaults[ ATTR_INDENT - ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_INDENT, 0 ); + ppPoolDefaults[ ATTR_VER_JUSTIFY - ATTR_STARTINDEX ] = new SvxVerJustifyItem( SVX_VER_JUSTIFY_STANDARD, ATTR_VER_JUSTIFY); + ppPoolDefaults[ ATTR_STACKED - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_STACKED, FALSE ); + ppPoolDefaults[ ATTR_ROTATE_VALUE - ATTR_STARTINDEX ] = new SfxInt32Item( ATTR_ROTATE_VALUE, 0 ); + ppPoolDefaults[ ATTR_ROTATE_MODE - ATTR_STARTINDEX ] = new SvxRotateModeItem( SVX_ROTATE_MODE_BOTTOM, ATTR_ROTATE_MODE ); + ppPoolDefaults[ ATTR_VERTICAL_ASIAN - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_VERTICAL_ASIAN ); + // The default for the ATTR_WRITINGDIR cell attribute must by FRMDIR_ENVIRONMENT, + // so that value is returned when asking for a default cell's attributes. + // The value from the page style is set as DefaultHorizontalTextDirection for the EditEngine. + ppPoolDefaults[ ATTR_WRITINGDIR - ATTR_STARTINDEX ] = new SvxFrameDirectionItem( FRMDIR_ENVIRONMENT, ATTR_WRITINGDIR ); + ppPoolDefaults[ ATTR_LINEBREAK - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_LINEBREAK ); + ppPoolDefaults[ ATTR_SHRINKTOFIT - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_SHRINKTOFIT ); + ppPoolDefaults[ ATTR_BORDER_TLBR - ATTR_STARTINDEX ] = new SvxLineItem( ATTR_BORDER_TLBR ); + ppPoolDefaults[ ATTR_BORDER_BLTR - ATTR_STARTINDEX ] = new SvxLineItem( ATTR_BORDER_BLTR ); + ppPoolDefaults[ ATTR_MARGIN - ATTR_STARTINDEX ] = new SvxMarginItem( ATTR_MARGIN ); + ppPoolDefaults[ ATTR_MERGE - ATTR_STARTINDEX ] = new ScMergeAttr; + ppPoolDefaults[ ATTR_MERGE_FLAG - ATTR_STARTINDEX ] = new ScMergeFlagAttr; + ppPoolDefaults[ ATTR_VALUE_FORMAT - ATTR_STARTINDEX ] = new SfxUInt32Item( ATTR_VALUE_FORMAT, 0 ); + ppPoolDefaults[ ATTR_LANGUAGE_FORMAT - ATTR_STARTINDEX ] = new SvxLanguageItem( ScGlobal::eLnge, ATTR_LANGUAGE_FORMAT ); + ppPoolDefaults[ ATTR_BACKGROUND - ATTR_STARTINDEX ] = new SvxBrushItem( Color(COL_TRANSPARENT), ATTR_BACKGROUND ); + ppPoolDefaults[ ATTR_PROTECTION - ATTR_STARTINDEX ] = new ScProtectionAttr; + ppPoolDefaults[ ATTR_BORDER - ATTR_STARTINDEX ] = new SvxBoxItem( ATTR_BORDER ); + ppPoolDefaults[ ATTR_BORDER_INNER - ATTR_STARTINDEX ] = pGlobalBorderInnerAttr; + ppPoolDefaults[ ATTR_SHADOW - ATTR_STARTINDEX ] = new SvxShadowItem( ATTR_SHADOW ); + ppPoolDefaults[ ATTR_VALIDDATA - ATTR_STARTINDEX ] = new SfxUInt32Item( ATTR_VALIDDATA, 0 ); + ppPoolDefaults[ ATTR_CONDITIONAL - ATTR_STARTINDEX ] = new SfxUInt32Item( ATTR_CONDITIONAL, 0 ); + + // GetRscString funktioniert erst nach ScGlobal::Init, zu erkennen am EmptyBrushItem + //! zusaetzliche Methode ScGlobal::IsInit() oder so... + //! oder erkennen, ob dies der Secondary-Pool fuer einen MessagePool ist + if ( ScGlobal::GetEmptyBrushItem() ) + ppPoolDefaults[ ATTR_PATTERN - ATTR_STARTINDEX ] = new ScPatternAttr( pSet, ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ); + else + ppPoolDefaults[ ATTR_PATTERN - ATTR_STARTINDEX ] = new ScPatternAttr( pSet, + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(STRING_STANDARD)) ); //! without name? + + ppPoolDefaults[ ATTR_LRSPACE - ATTR_STARTINDEX ] = new SvxLRSpaceItem( ATTR_LRSPACE ); + ppPoolDefaults[ ATTR_ULSPACE - ATTR_STARTINDEX ] = new SvxULSpaceItem( ATTR_ULSPACE ); + ppPoolDefaults[ ATTR_PAGE - ATTR_STARTINDEX ] = new SvxPageItem( ATTR_PAGE ); + ppPoolDefaults[ ATTR_PAGE_PAPERTRAY - ATTR_STARTINDEX ] = new SfxAllEnumItem( ATTR_PAGE_PAPERTRAY ); + ppPoolDefaults[ ATTR_PAGE_PAPERBIN - ATTR_STARTINDEX ] = new SvxPaperBinItem( ATTR_PAGE_PAPERBIN ); + ppPoolDefaults[ ATTR_PAGE_SIZE - ATTR_STARTINDEX ] = new SvxSizeItem( ATTR_PAGE_SIZE ); + ppPoolDefaults[ ATTR_PAGE_MAXSIZE - ATTR_STARTINDEX ] = new SvxSizeItem( ATTR_PAGE_MAXSIZE ); + ppPoolDefaults[ ATTR_PAGE_HORCENTER - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_HORCENTER ); + ppPoolDefaults[ ATTR_PAGE_VERCENTER - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_VERCENTER ); + ppPoolDefaults[ ATTR_PAGE_ON - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_ON, TRUE ); + ppPoolDefaults[ ATTR_PAGE_DYNAMIC - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_DYNAMIC, TRUE ); + ppPoolDefaults[ ATTR_PAGE_SHARED - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_SHARED, TRUE ); + ppPoolDefaults[ ATTR_PAGE_NOTES - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_NOTES, FALSE ); + ppPoolDefaults[ ATTR_PAGE_GRID - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_GRID, FALSE ); + ppPoolDefaults[ ATTR_PAGE_HEADERS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_HEADERS, FALSE ); + ppPoolDefaults[ ATTR_PAGE_CHARTS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_CHARTS ); + ppPoolDefaults[ ATTR_PAGE_OBJECTS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_OBJECTS ); + ppPoolDefaults[ ATTR_PAGE_DRAWINGS - ATTR_STARTINDEX ] = new ScViewObjectModeItem( ATTR_PAGE_DRAWINGS ); + ppPoolDefaults[ ATTR_PAGE_TOPDOWN - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_TOPDOWN, TRUE ); + ppPoolDefaults[ ATTR_PAGE_SCALE - ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_SCALE, 100 ); + ppPoolDefaults[ ATTR_PAGE_SCALETOPAGES-ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, 1 ); + ppPoolDefaults[ ATTR_PAGE_FIRSTPAGENO- ATTR_STARTINDEX ] = new SfxUInt16Item( ATTR_PAGE_FIRSTPAGENO, 1 ); + ppPoolDefaults[ ATTR_PAGE_PRINTAREA - ATTR_STARTINDEX ] = new ScRangeItem( ATTR_PAGE_PRINTAREA ); + ppPoolDefaults[ ATTR_PAGE_REPEATROW - ATTR_STARTINDEX ] = new ScRangeItem( ATTR_PAGE_REPEATROW ); + ppPoolDefaults[ ATTR_PAGE_REPEATCOL - ATTR_STARTINDEX ] = new ScRangeItem( ATTR_PAGE_REPEATCOL ); + ppPoolDefaults[ ATTR_PAGE_PRINTTABLES- ATTR_STARTINDEX ] = new ScTableListItem( ATTR_PAGE_PRINTTABLES ); + ppPoolDefaults[ ATTR_PAGE_HEADERLEFT - ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_HEADERLEFT ); + ppPoolDefaults[ ATTR_PAGE_FOOTERLEFT - ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_FOOTERLEFT ); + ppPoolDefaults[ ATTR_PAGE_HEADERRIGHT- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_HEADERRIGHT ); + ppPoolDefaults[ ATTR_PAGE_FOOTERRIGHT- ATTR_STARTINDEX ] = new ScPageHFItem( ATTR_PAGE_FOOTERRIGHT ); + ppPoolDefaults[ ATTR_PAGE_HEADERSET - ATTR_STARTINDEX ] = new SvxSetItem( ATTR_PAGE_HEADERSET, aSetItemItemSet ); + ppPoolDefaults[ ATTR_PAGE_FOOTERSET - ATTR_STARTINDEX ] = new SvxSetItem( ATTR_PAGE_FOOTERSET, aSetItemItemSet ); + ppPoolDefaults[ ATTR_PAGE_FORMULAS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_FORMULAS, FALSE ); + ppPoolDefaults[ ATTR_PAGE_NULLVALS - ATTR_STARTINDEX ] = new SfxBoolItem( ATTR_PAGE_NULLVALS, TRUE ); + ppPoolDefaults[ ATTR_PAGE_SCALETO - ATTR_STARTINDEX ] = new ScPageScaleToItem( 1, 1 ); +// ppPoolDefaults[ ATTR_ITEM_DOUBLE - ATTR_STARTINDEX ] = new ScDoubleItem( ATTR_ITEM_DOUBLE, 0 ); + + SetDefaults( ppPoolDefaults ); + + if ( pSecondary ) + SetSecondaryPool( pSecondary ); + + // ATTR_LANGUAGE_FORMAT ab sv329 eingefuegt, VersionMap in _ScGlobal__Init + SetVersionMap( 1, 100, 157, pVersionMap1 ); + + // ATTR_VALIDDATA, ATTR_CONDITIONAL ab 341 + SetVersionMap( 2, 100, 158, pVersionMap2 ); + + // ATTR_INDENT ab 350 + SetVersionMap( 3, 100, 160, pVersionMap3 ); + + // ATTR_ROTATE_VALUE, ATTR_ROTATE_MODE ab 367 + SetVersionMap( 4, 100, 161, pVersionMap4 ); + + // CJK, CTL, EMPHASISMARK, TWOLINES from 614 + SetVersionMap( 5, 100, 163, pVersionMap5 ); + + // ATTR_SCRIPTSPACE, ATTR_HANGPUNCTUATION, ATTR_FORBIDDEN_RULES from 614d + SetVersionMap( 6, 100, 175, pVersionMap6 ); + + // ATTR_FONT_WORDLINE, ATTR_FONT_RELIEF, ATTR_HYPHENATE from 632b + SetVersionMap( 7, 100, 178, pVersionMap7 ); + + // ATTR_VERTICAL_ASIAN from 642q + SetVersionMap( 8, 100, 181, pVersionMap8 ); + + // ATTR_WRITINGDIR from 643y + SetVersionMap( 9, 100, 182, pVersionMap9 ); + + // ATTR_PAGE_SCALETO added in 680/sab008 + // new version map not required + + // ATTR_SHRINKTOFIT, ATTR_BORDER_TL_BR, ATTR_BORDER_BL_TR added in 680/dr14 + SetVersionMap( 10, 100, 184, pVersionMap10 ); + + // ATTR_FONT_OVERLINE added in DEV300/overline2 + SetVersionMap( 11, 100, 187, pVersionMap11 ); +} + +__EXPORT ScDocumentPool::~ScDocumentPool() +{ + Delete(); + + for ( USHORT i=0; i < ATTR_ENDINDEX-ATTR_STARTINDEX+1; i++ ) + { + SetRefCount( *ppPoolDefaults[i], 0 ); + delete ppPoolDefaults[i]; + } + + delete[] ppPoolDefaults; + SfxItemPool::Free(pSecondary); +} + +void ScDocumentPool::InitVersionMaps() +{ + DBG_ASSERT( !pVersionMap1 && !pVersionMap2 && + !pVersionMap3 && !pVersionMap4 && + !pVersionMap5 && !pVersionMap6 && + !pVersionMap7 && !pVersionMap8 && + !pVersionMap9 && !pVersionMap10 && + !pVersionMap11, "InitVersionMaps call multiple times" ); + + // alte WhichId's mappen + // nicht mit ATTR_* zaehlen, falls die sich nochmal aendern + + // erste Map: ATTR_LANGUAGE_FORMAT ab sv329 eingefuegt + + const USHORT nMap1Start = 100; // alter ATTR_STARTINDEX + const USHORT nMap1End = 157; // alter ATTR_ENDINDEX + const USHORT nMap1Count = nMap1End - nMap1Start + 1; + const USHORT nMap1New = 18; // ATTR_LANGUAGE_FORMAT - ATTR_STARTINDEX + pVersionMap1 = new USHORT [ nMap1Count ]; + USHORT i, j; + for ( i=0, j=nMap1Start; i < nMap1New; i++, j++ ) + pVersionMap1[i] = j; + // ein Eintrag eingefuegt... + for ( i=nMap1New, j=nMap1Start+nMap1New+1; i < nMap1Count; i++, j++ ) + pVersionMap1[i] = j; + + // zweite Map: ATTR_VALIDDATA und ATTR_CONDITIONAL ab 341 eingefuegt + + const USHORT nMap2Start = 100; // ATTR_STARTINDEX + const USHORT nMap2End = 158; // ATTR_ENDINDEX + const USHORT nMap2Count = nMap2End - nMap2Start + 1; + const USHORT nMap2New = 24; // ATTR_VALIDDATA - ATTR_STARTINDEX + pVersionMap2 = new USHORT [ nMap2Count ]; + for ( i=0, j=nMap2Start; i < nMap2New; i++, j++ ) + pVersionMap2[i] = j; + // zwei Eintraege eingefuegt... + for ( i=nMap2New, j=nMap2Start+nMap2New+2; i < nMap2Count; i++, j++ ) + pVersionMap2[i] = j; + + // dritte Map: ATTR_INDENT ab 350 eingefuegt + + const USHORT nMap3Start = 100; // ATTR_STARTINDEX + const USHORT nMap3End = 160; // ATTR_ENDINDEX + const USHORT nMap3Count = nMap3End - nMap3Start + 1; + const USHORT nMap3New = 11; // ATTR_INDENT - ATTR_STARTINDEX + pVersionMap3 = new USHORT [ nMap3Count ]; + for ( i=0, j=nMap3Start; i < nMap3New; i++, j++ ) + pVersionMap3[i] = j; + // ein Eintrag eingefuegt... + for ( i=nMap3New, j=nMap3Start+nMap3New+1; i < nMap3Count; i++, j++ ) + pVersionMap3[i] = j; + + // vierte Map: ATTR_ROTATE_VALUE und ATTR_ROTATE_MODE ab 367 eingefuegt + + const USHORT nMap4Start = 100; // ATTR_STARTINDEX + const USHORT nMap4End = 161; // ATTR_ENDINDEX + const USHORT nMap4Count = nMap4End - nMap4Start + 1; + const USHORT nMap4New = 14; // ATTR_ROTATE_VALUE - ATTR_STARTINDEX + pVersionMap4 = new USHORT [ nMap4Count ]; + for ( i=0, j=nMap4Start; i < nMap4New; i++, j++ ) + pVersionMap4[i] = j; + // zwei Eintraege eingefuegt... + for ( i=nMap4New, j=nMap4Start+nMap4New+2; i < nMap4Count; i++, j++ ) + pVersionMap4[i] = j; + + // fifth map: CJK..., CTL..., EMPHASISMARK, TWOLINES (12 items) added in 614 + + const USHORT nMap5Start = 100; // ATTR_STARTINDEX + const USHORT nMap5End = 163; // ATTR_ENDINDEX + const USHORT nMap5Count = nMap5End - nMap5Start + 1; + const USHORT nMap5New = 10; // ATTR_CJK_FONT - ATTR_STARTINDEX + pVersionMap5 = new USHORT [ nMap5Count ]; + for ( i=0, j=nMap5Start; i < nMap5New; i++, j++ ) + pVersionMap5[i] = j; + // 12 entries inserted + for ( i=nMap5New, j=nMap5Start+nMap5New+12; i < nMap5Count; i++, j++ ) + pVersionMap5[i] = j; + + // sixth map: ATTR_SCRIPTSPACE, ATTR_HANGPUNCTUATION, ATTR_FORBIDDEN_RULES added in 614d + + const USHORT nMap6Start = 100; // ATTR_STARTINDEX + const USHORT nMap6End = 175; // ATTR_ENDINDEX + const USHORT nMap6Count = nMap6End - nMap6Start + 1; + const USHORT nMap6New = 22; // ATTR_SCRIPTSPACE - ATTR_STARTINDEX + pVersionMap6 = new USHORT [ nMap6Count ]; + for ( i=0, j=nMap6Start; i < nMap6New; i++, j++ ) + pVersionMap6[i] = j; + // 3 entries inserted + for ( i=nMap6New, j=nMap6Start+nMap6New+3; i < nMap6Count; i++, j++ ) + pVersionMap6[i] = j; + + // seventh map: ATTR_FONT_WORDLINE, ATTR_FONT_RELIEF, ATTR_HYPHENATE added in 632b + + const USHORT nMap7Start = 100; // ATTR_STARTINDEX + const USHORT nMap7End = 178; // ATTR_ENDINDEX + const USHORT nMap7Count = nMap7End - nMap7Start + 1; + const USHORT nMap7New = 22; // ATTR_FONT_WORDLINE - ATTR_STARTINDEX + pVersionMap7 = new USHORT [ nMap7Count ]; + for ( i=0, j=nMap7Start; i < nMap7New; i++, j++ ) + pVersionMap7[i] = j; + // 3 entries inserted + for ( i=nMap7New, j=nMap7Start+nMap7New+3; i < nMap7Count; i++, j++ ) + pVersionMap7[i] = j; + + // eighth map: ATTR_VERTICAL_ASIAN added in 642q + + const USHORT nMap8Start = 100; // ATTR_STARTINDEX + const USHORT nMap8End = 181; // ATTR_ENDINDEX + const USHORT nMap8Count = nMap8End - nMap8Start + 1; + const USHORT nMap8New = 34; // ATTR_VERTICAL_ASIAN - ATTR_STARTINDEX + pVersionMap8 = new USHORT [ nMap8Count ]; + for ( i=0, j=nMap8Start; i < nMap8New; i++, j++ ) + pVersionMap8[i] = j; + // 1 entry inserted + for ( i=nMap8New, j=nMap8Start+nMap8New+1; i < nMap8Count; i++, j++ ) + pVersionMap8[i] = j; + + // 9th map: ATTR_WRITINGDIR added in 643y + + const USHORT nMap9Start = 100; // ATTR_STARTINDEX + const USHORT nMap9End = 182; // ATTR_ENDINDEX + const USHORT nMap9Count = nMap9End - nMap9Start + 1; + const USHORT nMap9New = 35; // ATTR_WRITINGDIR - ATTR_STARTINDEX + pVersionMap9 = new USHORT [ nMap9Count ]; + for ( i=0, j=nMap9Start; i < nMap9New; i++, j++ ) + pVersionMap9[i] = j; + // 1 entry inserted + for ( i=nMap9New, j=nMap9Start+nMap9New+1; i < nMap9Count; i++, j++ ) + pVersionMap9[i] = j; + + // ATTR_PAGE_SCALETO added in 680/sab008 + + // 10th map: ATTR_SHRINKTOFIT, ATTR_BORDER_TL_BR, ATTR_BORDER_BL_TR added in 680/dr14 + + const USHORT nMap10Start = 100; // ATTR_STARTINDEX + const USHORT nMap10End = 184; // ATTR_ENDINDEX + const USHORT nMap10Count = nMap10End - nMap10Start + 1; + const USHORT nMap10New = 37; // ATTR_SHRINKTOFIT - ATTR_STARTINDEX + pVersionMap10 = new USHORT [ nMap10Count ]; + for ( i=0, j=nMap10Start; i < nMap10New; i++, j++ ) + pVersionMap10[i] = j; + // 3 entries inserted + for ( i=nMap10New, j=nMap10Start+nMap10New+3; i < nMap10Count; i++, j++ ) + pVersionMap10[i] = j; + + // 11th map: ATTR_FONT_OVERLINE added in DEV300/overline2 + + const USHORT nMap11Start = 100; // ATTR_STARTINDEX + const USHORT nMap11End = 187; // ATTR_ENDINDEX + const USHORT nMap11Count = nMap11End - nMap11Start + 1; + const USHORT nMap11New = 5; // ATTR_FONT_OVERLINE - ATTR_STARTINDEX + pVersionMap11 = new USHORT [ nMap11Count ]; + for ( i=0, j=nMap11Start; i < nMap11New; i++, j++ ) + pVersionMap11[i] = j; + // 1 entry inserted + for ( i=nMap11New, j=nMap11Start+nMap11New+1; i < nMap11Count; i++, j++ ) + pVersionMap11[i] = j; +} + +void ScDocumentPool::DeleteVersionMaps() +{ + DBG_ASSERT( pVersionMap1 && pVersionMap2 && + pVersionMap3 && pVersionMap4 && + pVersionMap5 && pVersionMap6 && + pVersionMap7 && pVersionMap8 && + pVersionMap9 && pVersionMap10 && + pVersionMap11, "DeleteVersionMaps without maps" ); + + delete[] pVersionMap11; + pVersionMap11 = 0; + delete[] pVersionMap10; + pVersionMap10 = 0; + delete[] pVersionMap9; + pVersionMap9 = 0; + delete[] pVersionMap8; + pVersionMap8 = 0; + delete[] pVersionMap7; + pVersionMap7 = 0; + delete[] pVersionMap6; + pVersionMap6 = 0; + delete[] pVersionMap5; + pVersionMap5 = 0; + delete[] pVersionMap4; + pVersionMap4 = 0; + delete[] pVersionMap3; + pVersionMap3 = 0; + delete[] pVersionMap2; + pVersionMap2 = 0; + delete[] pVersionMap1; + pVersionMap1 = 0; +} + +// ---------------------------------------------------------------------------------------- +// +// Fuer die Pattern-Attribute (SetItems) kann der USHORT RefCount leicht ueberlaufen +// (z.B. 600 ganze Zeilen abwechselnd formatieren). +// Darum wird der RefCount bei SC_MAX_POOLREF festgehalten und nicht mehr hoch- oder +// heruntergezaehlt. Dieser RefCount wird dann erst beim naechsten Laden neu gezaehlt. +// Die Differenz zwischen SC_MAX_POOLREF und SC_SAFE_POOLREF ist ein wenig groesser +// als noetig, um zu erkennen, wenn der RefCount aus Versehen doch "normal" veraendert +// wird (Assertions). +// + +const SfxPoolItem& __EXPORT ScDocumentPool::Put( const SfxPoolItem& rItem, USHORT nWhich ) +{ + if ( rItem.Which() != ATTR_PATTERN ) // nur Pattern ist special + return SfxItemPool::Put( rItem, nWhich ); + + // das Default-Pattern dieses Pools nicht kopieren + if (&rItem == ppPoolDefaults[ ATTR_PATTERN - ATTR_STARTINDEX ]) + return rItem; + + // ansonsten muss Put immer passieren, weil es ein anderer Pool sein kann + const SfxPoolItem& rNew = SfxItemPool::Put( rItem, nWhich ); + CheckRef( rNew ); + return rNew; +} + +void __EXPORT ScDocumentPool::Remove( const SfxPoolItem& rItem ) +{ + if ( rItem.Which() == ATTR_PATTERN ) // nur Pattern ist special + { + ULONG nRef = rItem.GetRefCount(); + if ( nRef >= (ULONG) SC_MAX_POOLREF && nRef <= (ULONG) SFX_ITEMS_OLD_MAXREF ) + { + if ( nRef != (ULONG) SC_SAFE_POOLREF ) + { + DBG_ERROR("Wer fummelt da an meinen Ref-Counts herum"); + SetRefCount( (SfxPoolItem&)rItem, (ULONG) SC_SAFE_POOLREF ); + } + return; // nicht herunterzaehlen + } + } + SfxItemPool::Remove( rItem ); +} + +void ScDocumentPool::CheckRef( const SfxPoolItem& rItem ) // static +{ + ULONG nRef = rItem.GetRefCount(); + if ( nRef >= (ULONG) SC_MAX_POOLREF && nRef <= (ULONG) SFX_ITEMS_OLD_MAXREF ) + { + // beim Apply vom Cache wird evtl. um 2 hochgezaehlt (auf MAX+1 oder SAFE+2), + // heruntergezaehlt wird nur einzeln (in LoadCompleted) + DBG_ASSERT( nRef<=(ULONG)SC_MAX_POOLREF+1 || (nRef>=(ULONG)SC_SAFE_POOLREF-1 && nRef<=(ULONG)SC_SAFE_POOLREF+2), + "ScDocumentPool::CheckRef" ); + SetRefCount( (SfxPoolItem&)rItem, (ULONG) SC_SAFE_POOLREF ); + } +} + +// ---------------------------------------------------------------------------------------- + +void ScDocumentPool::StyleDeleted( ScStyleSheet* pStyle ) +{ + USHORT nCount = GetItemCount(ATTR_PATTERN); + for (USHORT i=0; i<nCount; i++) + { + ScPatternAttr* pPattern = (ScPatternAttr*)GetItem(ATTR_PATTERN, i); + if ( pPattern && pPattern->GetStyleSheet() == pStyle ) + pPattern->StyleToName(); + } +} + +SfxItemPool* __EXPORT ScDocumentPool::Clone() const +{ + return new SfxItemPool (*this, TRUE); +} + +SfxItemPresentation lcl_HFPresentation +( + const SfxPoolItem& rItem, + SfxItemPresentation ePresentation, + SfxMapUnit eCoreMetric, + SfxMapUnit ePresentationMetric, + String& rText, + const IntlWrapper* pIntl +) +{ + const SfxItemSet& rSet = ((const SfxSetItem&)rItem).GetItemSet(); + const SfxPoolItem* pItem; + + if ( SFX_ITEM_SET == rSet.GetItemState(ATTR_PAGE_ON,FALSE,&pItem) ) + { + if( FALSE == ((const SfxBoolItem*)pItem)->GetValue() ) + return SFX_ITEM_PRESENTATION_NONE; + } + + SfxItemIter aIter( rSet ); + pItem = aIter.FirstItem(); + String aText; + String aDel = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM( " + " )); + + while( pItem ) + { + USHORT nWhich = pItem->Which(); + + aText.Erase(); + + switch( nWhich ) + { + case ATTR_PAGE_ON: + case ATTR_PAGE_DYNAMIC: + case ATTR_PAGE_SHARED: + break; + + case ATTR_LRSPACE: + { + SvxLRSpaceItem& rLRItem = (SvxLRSpaceItem&)*pItem; + USHORT nPropLeftMargin = rLRItem.GetPropLeft(); + USHORT nPropRightMargin = rLRItem.GetPropRight(); + USHORT nLeftMargin, nRightMargin; + long nTmp; + nTmp = rLRItem.GetLeft(); + nLeftMargin = nTmp < 0 ? 0 : USHORT(nTmp); + nTmp = rLRItem.GetRight(); + nRightMargin = nTmp < 0 ? 0 : USHORT(nTmp); + + aText = SVX_RESSTR(RID_SVXITEMS_LRSPACE_LEFT); + if ( 100 != nPropLeftMargin ) + { + aText += String::CreateFromInt32( nPropLeftMargin ); + aText += '%'; + } + else + { + aText += GetMetricText( (long)nLeftMargin, + eCoreMetric, ePresentationMetric, pIntl ); + aText += SVX_RESSTR(GetMetricId(ePresentationMetric)); + } + aText += cpDelim; + + // nPropFirstLineOfst haben wir nicht + + aText += SVX_RESSTR(RID_SVXITEMS_LRSPACE_RIGHT); + if ( 100 != nPropRightMargin ) + { + aText += String::CreateFromInt32( nPropRightMargin ); + aText += '%'; + } + else + { + aText += GetMetricText( (long)nRightMargin, + eCoreMetric, ePresentationMetric, pIntl ); + aText += SVX_RESSTR(GetMetricId(ePresentationMetric)); + } + } + break; + + default: + if ( !pIntl ) + pIntl = ScGlobal::GetScIntlWrapper(); + pItem->GetPresentation( ePresentation, eCoreMetric, ePresentationMetric, aText, pIntl ); + + } + + if ( aText.Len() ) + { + rText += aText; + rText += aDel; + } + + pItem = aIter.NextItem(); + } + + rText.EraseTrailingChars(); + rText.EraseTrailingChars( '+' ); + rText.EraseTrailingChars(); + + return ePresentation; +} + +SfxItemPresentation __EXPORT ScDocumentPool::GetPresentation( + const SfxPoolItem& rItem, + SfxItemPresentation ePresentation, + SfxMapUnit ePresentationMetric, + String& rText, + const IntlWrapper* pIntl ) const +{ + USHORT nW = rItem.Which(); + String aStrYes ( ScGlobal::GetRscString(STR_YES) ); + String aStrNo ( ScGlobal::GetRscString(STR_NO) ); + String aStrSep = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(": ")); + + switch( nW ) + { + case ATTR_PAGE_TOPDOWN: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_PRINTDIR); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? + ScGlobal::GetRscString(STR_SCATTR_PAGE_TOPDOWN) : + ScGlobal::GetRscString(STR_SCATTR_PAGE_LEFTRIGHT) ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_HEADERS: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_HEADERS); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? aStrYes : aStrNo ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_NULLVALS: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_NULLVALS); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? aStrYes : aStrNo ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_FORMULAS: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_FORMULAS); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? aStrYes : aStrNo ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_NOTES: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_NOTES); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? aStrYes : aStrNo ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_GRID: + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_GRID); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += ((const SfxBoolItem&)rItem).GetValue() ? aStrYes : aStrNo ; + break; + default: + { + // added to avoid warnings + } + } + break; + + case ATTR_PAGE_SCALETOPAGES: + { + USHORT nPagNo = ((const SfxUInt16Item&)rItem).GetValue(); + + if( nPagNo ) + { + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + { + rText.Assign( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALETOPAGES ) ).Append( aStrSep ); + } +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + { + String aPages( ScGlobal::GetRscString( STR_SCATTR_PAGE_SCALE_PAGES ) ); + aPages.SearchAndReplaceAscii( "%1", String::CreateFromInt32( nPagNo ) ); + rText.Append( aPages ); + } + break; + default: + { + // added to avoid warnings + } + } + } + else + { + ePresentation = SFX_ITEM_PRESENTATION_NONE; + } + } + break; + + case ATTR_PAGE_FIRSTPAGENO: + { + USHORT nPagNo = ((const SfxUInt16Item&)rItem).GetValue(); + + if( nPagNo ) + { + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_FIRSTPAGENO); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += String::CreateFromInt32( nPagNo ); + break; + default: + { + // added to avoid warnings + } + } + } + else + { + ePresentation = SFX_ITEM_PRESENTATION_NONE; + } + } + break; + + case ATTR_PAGE_SCALE: + { + USHORT nPercent = ((const SfxUInt16Item&)rItem).GetValue(); + + if( nPercent ) + { + switch ( ePresentation ) + { + case SFX_ITEM_PRESENTATION_COMPLETE: + rText = ScGlobal::GetRscString(STR_SCATTR_PAGE_SCALE); + rText += aStrSep; +// break; // DURCHFALLEN!!! + case SFX_ITEM_PRESENTATION_NAMELESS: + rText += String::CreateFromInt32( nPercent ); + rText += '%'; + break; + default: + { + // added to avoid warnings + } + } + } + else + { + ePresentation = SFX_ITEM_PRESENTATION_NONE; + } + } + break; + + case ATTR_PAGE_HEADERSET: + { + String aBuffer; + + if( lcl_HFPresentation( rItem, ePresentation, GetMetric( nW ), ePresentationMetric, aBuffer, pIntl ) != SFX_ITEM_PRESENTATION_NONE ) + { + rText = ScGlobal::GetRscString(STR_HEADER); + rText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ( " )); + rText += aBuffer; + rText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ) " )); + } + } + break; + + case ATTR_PAGE_FOOTERSET: + { + String aBuffer; + + if( lcl_HFPresentation( rItem, ePresentation, GetMetric( nW ), ePresentationMetric, aBuffer, pIntl ) != SFX_ITEM_PRESENTATION_NONE ) + { + rText = ScGlobal::GetRscString(STR_FOOTER); + rText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ( " )); + rText += aBuffer; + rText.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ) " )); + } + } + break; + +/* + case ATTR_PAGE_HEADERLEFT: + rText = "SID_SCATTR_PAGE_HEADERLEFT"; + break; + + case ATTR_PAGE_FOOTERLEFT: + rText = "SID_SCATTR_PAGE_FOOTERLEFT"; + break; + + case ATTR_PAGE_HEADERRIGHT: + rText = "SID_SCATTR_PAGE_HEADERRIGHT"; + break; + + case ATTR_PAGE_FOOTERRIGHT: + rText = "SID_SCATTR_PAGE_FOOTERRIGHT"; + break; +*/ + + default: + if ( !pIntl ) + pIntl = ScGlobal::GetScIntlWrapper(); + ePresentation = rItem.GetPresentation( ePresentation, GetMetric( nW ), ePresentationMetric, rText, pIntl ); + break; + } + + return ePresentation; +} + +SfxMapUnit __EXPORT ScDocumentPool::GetMetric( USHORT nWhich ) const +{ + // eigene Attribute: Twips, alles andere 1/100 mm + + if ( nWhich >= ATTR_STARTINDEX && nWhich <= ATTR_ENDINDEX ) + return SFX_MAPUNIT_TWIP; + else + return SFX_MAPUNIT_100TH_MM; +} + + + + + diff --git a/sc/source/core/data/documen2.cxx b/sc/source/core/data/documen2.cxx new file mode 100644 index 000000000000..3404866446fe --- /dev/null +++ b/sc/source/core/data/documen2.cxx @@ -0,0 +1,1267 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen2.cxx,v $ + * $Revision: 1.75.18.1 $ + * + * 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 --------------------------------------------------------------- + +#define _ZFORLIST_DECLARE_TABLE +#include "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <svx/editeng.hxx> +#include <svx/forbiddencharacterstable.hxx> +#include <svx/linkmgr.hxx> +#include <svx/svdpool.hxx> +#include <svx/svdobj.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/printer.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/zformat.hxx> +#include <vcl/virdev.hxx> +#include <comphelper/processfactory.hxx> +#include <svtools/PasswordHelper.hxx> +#include <tools/tenccvt.hxx> +#include <tools/list.hxx> +#include <rtl/crc.h> + +#include "document.hxx" +#include "table.hxx" +#include "attrib.hxx" +#include "patattr.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "pivot.hxx" +#include "docpool.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "globstr.hrc" +#include "chartarr.hxx" +#include "chartlock.hxx" +#include "rechead.hxx" +#include "global.hxx" +#include "brdcst.hxx" +#include "bcaslot.hxx" +#include "adiasync.hxx" +#include "addinlis.hxx" +#include "chartlis.hxx" +#include "markdata.hxx" +#include "conditio.hxx" +#include "validat.hxx" +#include "progress.hxx" +#include "detdata.hxx" +#include "sc.hrc" // FID_DATACHANGED +#include "ddelink.hxx" +#include "chgtrack.hxx" +#include "chgviset.hxx" +#include "editutil.hxx" +#include "hints.hxx" +#include "dpobject.hxx" +#include "scrdata.hxx" +#include "poolhelp.hxx" +#include "unoreflist.hxx" +#include "listenercalls.hxx" +#include "recursionhelper.hxx" +#include "lookupcache.hxx" +#include "externalrefmgr.hxx" +#include "tabprotection.hxx" +#include "formulaparserpool.hxx" +#include "clipparam.hxx" + +// pImpl because including lookupcache.hxx in document.hxx isn't wanted, and +// dtor plus helpers are convenient. +struct ScLookupCacheMapImpl +{ + ScLookupCacheMap aCacheMap; + ~ScLookupCacheMapImpl() + { + freeCaches(); + } + void clear() + { + freeCaches(); + // Zap map. + ScLookupCacheMap aTmp; + aCacheMap.swap( aTmp); + } +private: + void freeCaches() + { + for (ScLookupCacheMap::iterator it( aCacheMap.begin()); it != aCacheMap.end(); ++it) + delete (*it).second; + } +}; + +// STATIC DATA ----------------------------------------------------------- + +ScDocument::ScDocument( ScDocumentMode eMode, + SfxObjectShell* pDocShell ) : + xServiceManager( ::comphelper::getProcessServiceFactory() ), + mpUndoManager( NULL ), + pEditEngine( NULL ), + pNoteEngine( NULL ), + pNoteItemPool( NULL ), + pShell( pDocShell ), + pPrinter( NULL ), + pVirtualDevice_100th_mm( NULL ), + pDrawLayer( NULL ), + pColorTable( NULL ), + pCondFormList( NULL ), + pValidationList( NULL ), + pFormatExchangeList( NULL ), + pDPCollection( NULL ), + pLinkManager( NULL ), + pFormulaTree( NULL ), + pEOFormulaTree( NULL ), + pFormulaTrack( NULL ), + pEOFormulaTrack( NULL ), + pOtherObjects( NULL ), + pClipData( NULL ), + pDetOpList(NULL), + pChangeTrack( NULL ), + pUnoBroadcaster( NULL ), + pUnoListenerCalls( NULL ), + pUnoRefUndoList( NULL ), + pChangeViewSettings( NULL ), + pScriptTypeData( NULL ), + pCacheFieldEditEngine( NULL ), + pDocProtection( NULL ), + mpClipParam( NULL), + pExternalRefMgr( NULL ), + pViewOptions( NULL ), + pDocOptions( NULL ), + pExtDocOptions( NULL ), + pConsolidateDlgData( NULL ), + pRecursionHelper( NULL ), + pAutoNameCache( NULL ), + pLookupCacheMapImpl( NULL ), + nUnoObjectId( 0 ), + nRangeOverflowType( 0 ), + aCurTextWidthCalcPos(MAXCOL,0,0), + nFormulaCodeInTree(0), + nXMLImportedFormulaCount( 0 ), + nInterpretLevel(0), + nMacroInterpretLevel(0), + nInterpreterTableOpLevel(0), + nMaxTableNumber( 0 ), + nSrcVer( SC_CURRENT_VERSION ), + nSrcMaxRow( MAXROW ), + nFormulaTrackCount(0), + nHardRecalcState(0), + nVisibleTab( 0 ), + eLinkMode(LM_UNKNOWN), + bAutoCalc( eMode == SCDOCMODE_DOCUMENT ), + bAutoCalcShellDisabled( FALSE ), + bForcedFormulaPending( FALSE ), + bCalculatingFormulaTree( FALSE ), + bIsClip( eMode == SCDOCMODE_CLIP ), + bIsUndo( eMode == SCDOCMODE_UNDO ), + bIsVisible( FALSE ), + bIsEmbedded( FALSE ), +// bNoSetDirty( TRUE ), + bNoSetDirty( FALSE ), + bInsertingFromOtherDoc( FALSE ), + bImportingXML( FALSE ), + bXMLFromWrapper( FALSE ), + bCalcingAfterLoad( FALSE ), + bNoListening( FALSE ), + bLoadingDone( TRUE ), + bIdleDisabled( FALSE ), + bInLinkUpdate( FALSE ), + bChartListenerCollectionNeedsUpdate( FALSE ), + bHasForcedFormulas( FALSE ), + bInDtorClear( FALSE ), + bExpandRefs( FALSE ), + bDetectiveDirty( FALSE ), + nMacroCallMode( SC_MACROCALL_ALLOWED ), + bHasMacroFunc( FALSE ), + nVisSpellState( 0 ), + nAsianCompression(SC_ASIANCOMPRESSION_INVALID), + nAsianKerning(SC_ASIANKERNING_INVALID), + bSetDrawDefaults( FALSE ), + bPastingDrawFromOtherDoc( FALSE ), + nInDdeLinkUpdate( 0 ), + bInUnoBroadcast( FALSE ), + bInUnoListenerCall( FALSE ), + eGrammar( formula::FormulaGrammar::GRAM_NATIVE ), + bStyleSheetUsageInvalid( TRUE ), + mbUndoEnabled( true ), + mbAdjustHeightEnabled( true ), + mbExecuteLinkEnabled( true ), + mbChangeReadOnlyEnabled( false ), + mbStreamValidLocked( false ), + mnNamedRangesLockCount( 0 ) +{ + SetStorageGrammar( formula::FormulaGrammar::GRAM_STORAGE_DEFAULT); + + eSrcSet = gsl_getSystemTextEncoding(); + + if ( eMode == SCDOCMODE_DOCUMENT ) + { + if ( pDocShell ) + pLinkManager = new SvxLinkManager( pDocShell ); + + xPoolHelper = new ScPoolHelper( this ); + + pTab[0] = NULL; + pBASM = new ScBroadcastAreaSlotMachine( this ); + pChartListenerCollection = new ScChartListenerCollection( this ); + pRefreshTimerControl = new ScRefreshTimerControl; + } + else + { + pTab[0] = NULL; + pBASM = NULL; + pChartListenerCollection = NULL; + pRefreshTimerControl = NULL; + } + + for (SCTAB i=1; i<=MAXTAB; i++) + pTab[i] = NULL; + + pRangeName = new ScRangeName( 4, 4, FALSE, this ); + pDBCollection = new ScDBCollection( 4, 4, FALSE, this ); + pSelectionAttr = NULL; + pChartCollection = new ScChartCollection; + apTemporaryChartLock = std::auto_ptr< ScTemporaryChartLock >( new ScTemporaryChartLock(this) ); + xColNameRanges = new ScRangePairList; + xRowNameRanges = new ScRangePairList; + ImplCreateOptions(); + // languages for a visible document are set by docshell later (from options) + SetLanguage( ScGlobal::eLnge, ScGlobal::eLnge, ScGlobal::eLnge ); + + aTrackTimer.SetTimeoutHdl( LINK( this, ScDocument, TrackTimeHdl ) ); + aTrackTimer.SetTimeout( 100 ); +} + +SvxLinkManager* ScDocument::GetLinkManager() const +{ + if ( bAutoCalc && !pLinkManager && pShell) + { + pLinkManager = new SvxLinkManager( pShell ); + } + return pLinkManager; +} + + +void ScDocument::SetStorageGrammar( formula::FormulaGrammar::Grammar eGram ) +{ + DBG_ASSERT( + eGram == formula::FormulaGrammar::GRAM_ODFF || + eGram == formula::FormulaGrammar::GRAM_PODF, + "ScDocument::SetStorageGrammar: wrong storage grammar"); + + eStorageGrammar = eGram; + + // FIXME: the XML import shouldn't strip brackets, the compiler should + // digest them instead, which could also speedup reference recognition + // during import. + + eXmlImportGrammar = formula::FormulaGrammar::mergeToGrammar( eGram, + formula::FormulaGrammar::CONV_OOO); +} + + +void ScDocument::SetDocVisible( BOOL bSet ) +{ + // called from view ctor - only for a visible document, + // each new sheet's RTL flag is initialized from the locale + bIsVisible = bSet; +} + + +sal_uInt32 ScDocument::GetDocumentID() const +{ + const ScDocument* pThis = this; + sal_uInt32 nCrc = rtl_crc32( 0, &pThis, sizeof(ScDocument*) ); + // the this pointer only might not be sufficient + nCrc = rtl_crc32( nCrc, &pShell, sizeof(SfxObjectShell*) ); + return nCrc; +} + + +void ScDocument::StartChangeTracking() +{ + if (!pChangeTrack) + pChangeTrack = new ScChangeTrack( this ); +} + +void ScDocument::EndChangeTracking() +{ + delete pChangeTrack; + pChangeTrack = NULL; +} + +void ScDocument::SetChangeTrack( ScChangeTrack* pTrack ) +{ + DBG_ASSERT( pTrack->GetDocument() == this, "SetChangeTrack: different documents" ); + if ( !pTrack || pTrack == pChangeTrack || pTrack->GetDocument() != this ) + return ; + EndChangeTracking(); + pChangeTrack = pTrack; +} + + +IMPL_LINK( ScDocument, TrackTimeHdl, Timer*, EMPTYARG ) +{ + if ( ScDdeLink::IsInUpdate() ) // nicht verschachteln + { + aTrackTimer.Start(); // spaeter nochmal versuchen + } + else if (pShell) // ausfuehren + { + TrackFormulas(); + pShell->Broadcast( SfxSimpleHint( FID_DATACHANGED ) ); + ResetChanged( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB) ); + + // modified... + + if (!pShell->IsModified()) + { + pShell->SetModified( TRUE ); + SfxBindings* pBindings = GetViewBindings(); + if (pBindings) + { + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + } + } + + return 0; +} + +void ScDocument::StartTrackTimer() +{ + if (!aTrackTimer.IsActive()) // nicht ewig aufschieben + aTrackTimer.Start(); +} + +ScDocument::~ScDocument() +{ + DBG_ASSERT( !bInLinkUpdate, "bInLinkUpdate in dtor" ); + + bInDtorClear = TRUE; + + // first of all disable all refresh timers by deleting the control + if ( pRefreshTimerControl ) + { // To be sure there isn't anything running do it with a protector, + // this ensures also that nothing needs the control anymore. + ScRefreshTimerProtector aProt( GetRefreshTimerControlAddress() ); + delete pRefreshTimerControl, pRefreshTimerControl = NULL; + } + + // Links aufrauemen + + if ( GetLinkManager() ) + { + // BaseLinks freigeben + for ( USHORT n = pLinkManager->GetServers().Count(); n; ) + pLinkManager->GetServers()[ --n ]->Closed(); + + if ( pLinkManager->GetLinks().Count() ) + pLinkManager->Remove( 0, pLinkManager->GetLinks().Count() ); + } + + mxFormulaParserPool.reset(); + // Destroy the external ref mgr instance here because it has a timer + // which needs to be stopped before the app closes. + pExternalRefMgr.reset(); + + ScAddInAsync::RemoveDocument( this ); + ScAddInListener::RemoveDocument( this ); + DELETEZ( pChartListenerCollection); // vor pBASM wg. evtl. Listener! + DELETEZ( pLookupCacheMapImpl); // before pBASM because of listeners + // BroadcastAreas vor allen Zellen zerstoeren um unnoetige + // Einzel-EndListenings der Formelzellen zu vermeiden + delete pBASM; // BroadcastAreaSlotMachine + pBASM = NULL; + + if (pUnoBroadcaster) + { + delete pUnoBroadcaster; // broadcasted nochmal SFX_HINT_DYING + pUnoBroadcaster = NULL; + } + + delete pUnoRefUndoList; + delete pUnoListenerCalls; + + Clear( sal_True ); // TRUE = from destructor (needed for SdrModel::ClearModel) + + if (pCondFormList) + { + pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() ); + DELETEZ(pCondFormList); + } + if (pValidationList) + { + pValidationList->DeleteAndDestroy( 0, pValidationList->Count() ); + DELETEZ(pValidationList); + } + delete pRangeName; + delete pDBCollection; + delete pSelectionAttr; + apTemporaryChartLock.reset(); + delete pChartCollection; + DeleteDrawLayer(); + delete pFormatExchangeList; + delete pPrinter; + ImplDeleteOptions(); + delete pConsolidateDlgData; + delete pLinkManager; + delete pClipData; + delete pDetOpList; // loescht auch die Eintraege + delete pChangeTrack; + delete pEditEngine; + delete pNoteEngine; + SfxItemPool::Free(pNoteItemPool); + delete pChangeViewSettings; // und weg damit + delete pVirtualDevice_100th_mm; + + delete pDPCollection; + + // delete the EditEngine before destroying the xPoolHelper + delete pCacheFieldEditEngine; + + if ( xPoolHelper.isValid() && !bIsClip ) + xPoolHelper->SourceDocumentGone(); + xPoolHelper.unbind(); + + DeleteColorTable(); + delete pScriptTypeData; + delete pOtherObjects; + delete pRecursionHelper; + + DBG_ASSERT( !pAutoNameCache, "AutoNameCache still set in dtor" ); +} + +void ScDocument::InitClipPtrs( ScDocument* pSourceDoc ) +{ + DBG_ASSERT(bIsClip, "InitClipPtrs und nicht bIsClip"); + + if (pCondFormList) + { + pCondFormList->DeleteAndDestroy( 0, pCondFormList->Count() ); + DELETEZ(pCondFormList); + } + if (pValidationList) + { + pValidationList->DeleteAndDestroy( 0, pValidationList->Count() ); + DELETEZ(pValidationList); + } + + Clear(); + + xPoolHelper = pSourceDoc->xPoolHelper; + + // bedingte Formate / Gueltigkeiten + //! Vorlagen kopieren? + const ScConditionalFormatList* pSourceCond = pSourceDoc->pCondFormList; + if ( pSourceCond ) + pCondFormList = new ScConditionalFormatList(this, *pSourceCond); + const ScValidationDataList* pSourceValid = pSourceDoc->pValidationList; + if ( pSourceValid ) + pValidationList = new ScValidationDataList(this, *pSourceValid); + + // Links in Stream speichern + delete pClipData; + if (pSourceDoc->HasDdeLinks()) + { + pClipData = new SvMemoryStream; + pSourceDoc->SaveDdeLinks(*pClipData); + } + else + pClipData = NULL; + + // Options pointers exist (ImplCreateOptions) for any document. + // Must be copied for correct results in OLE objects (#i42666#). + SetDocOptions( pSourceDoc->GetDocOptions() ); + SetViewOptions( pSourceDoc->GetViewOptions() ); +} + +SvNumberFormatter* ScDocument::GetFormatTable() const +{ + return xPoolHelper->GetFormTable(); +} + +SfxItemPool* ScDocument::GetEditPool() const +{ + return xPoolHelper->GetEditPool(); +} + +SfxItemPool* ScDocument::GetEnginePool() const +{ + return xPoolHelper->GetEnginePool(); +} + +ScFieldEditEngine& ScDocument::GetEditEngine() +{ + if ( !pEditEngine ) + { + pEditEngine = new ScFieldEditEngine( GetEnginePool(), GetEditPool() ); + pEditEngine->SetUpdateMode( FALSE ); + pEditEngine->EnableUndo( FALSE ); + pEditEngine->SetRefMapMode( MAP_100TH_MM ); + pEditEngine->SetForbiddenCharsTable( xForbiddenCharacters ); + } + return *pEditEngine; +} + +ScNoteEditEngine& ScDocument::GetNoteEngine() +{ + if ( !pNoteEngine ) + { + pNoteEngine = new ScNoteEditEngine( GetEnginePool(), GetEditPool() ); + pNoteEngine->SetUpdateMode( FALSE ); + pNoteEngine->EnableUndo( FALSE ); + pNoteEngine->SetRefMapMode( MAP_100TH_MM ); + pNoteEngine->SetForbiddenCharsTable( xForbiddenCharacters ); + const SfxItemSet& rItemSet = GetDefPattern()->GetItemSet(); + SfxItemSet* pEEItemSet = new SfxItemSet( pNoteEngine->GetEmptyItemSet() ); + ScPatternAttr::FillToEditItemSet( *pEEItemSet, rItemSet ); + pNoteEngine->SetDefaults( pEEItemSet ); // edit engine takes ownership + } + return *pNoteEngine; +} + +//UNUSED2009-05 SfxItemPool& ScDocument::GetNoteItemPool() +//UNUSED2009-05 { +//UNUSED2009-05 if ( !pNoteItemPool ) +//UNUSED2009-05 pNoteItemPool = new SfxItemPool(SdrObject::GetGlobalDrawObjectItemPool()); +//UNUSED2009-05 return *pNoteItemPool; +//UNUSED2009-05 } + +void ScDocument::ResetClip( ScDocument* pSourceDoc, const ScMarkData* pMarks ) +{ + if (bIsClip) + { + InitClipPtrs(pSourceDoc); + + for (SCTAB i = 0; i <= MAXTAB; i++) + if (pSourceDoc->pTab[i]) + if (!pMarks || pMarks->GetTableSelect(i)) + { + String aString; + pSourceDoc->pTab[i]->GetName(aString); + pTab[i] = new ScTable(this, i, aString); + pTab[i]->SetLayoutRTL( pSourceDoc->pTab[i]->IsLayoutRTL() ); + nMaxTableNumber = i+1; + } + } + else + { + DBG_ERROR("ResetClip"); + } +} + +void ScDocument::ResetClip( ScDocument* pSourceDoc, SCTAB nTab ) +{ + if (bIsClip) + { + InitClipPtrs(pSourceDoc); + + pTab[nTab] = new ScTable(this, nTab, + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("baeh"))); + if (pSourceDoc->pTab[nTab]) + pTab[nTab]->SetLayoutRTL( pSourceDoc->pTab[nTab]->IsLayoutRTL() ); + nMaxTableNumber = nTab+1; + } + else + { + DBG_ERROR("ResetClip"); + } +} + +void ScDocument::DeleteNumberFormat( const sal_uInt32* /* pDelKeys */, sal_uInt32 /* nCount */ ) +{ +/* + for (ULONG i = 0; i < nCount; i++) + xPoolHelper->GetFormTable()->DeleteEntry(pDelKeys[i]); +*/ +} + +void ScDocument::PutCell( SCCOL nCol, SCROW nRow, SCTAB nTab, + ScBaseCell* pCell, ULONG nFormatIndex, BOOL bForceTab ) +{ + if (VALIDTAB(nTab)) + { + if ( bForceTab && !pTab[nTab] ) + { + BOOL bExtras = !bIsUndo; // Spaltenbreiten, Zeilenhoehen, Flags + + pTab[nTab] = new ScTable(this, nTab, + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("temp")), + bExtras, bExtras); + } + + if (pTab[nTab]) + pTab[nTab]->PutCell( nCol, nRow, nFormatIndex, pCell ); + } +} + +//UNUSED2009-05 void ScDocument::PutCell( const ScAddress& rPos, ScBaseCell* pCell, +//UNUSED2009-05 ULONG nFormatIndex, BOOL bForceTab ) +//UNUSED2009-05 { +//UNUSED2009-05 SCTAB nTab = rPos.Tab(); +//UNUSED2009-05 if ( bForceTab && !pTab[nTab] ) +//UNUSED2009-05 { +//UNUSED2009-05 BOOL bExtras = !bIsUndo; // Spaltenbreiten, Zeilenhoehen, Flags +//UNUSED2009-05 +//UNUSED2009-05 pTab[nTab] = new ScTable(this, nTab, +//UNUSED2009-05 String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("temp")), +//UNUSED2009-05 bExtras, bExtras); +//UNUSED2009-05 } +//UNUSED2009-05 +//UNUSED2009-05 if (pTab[nTab]) +//UNUSED2009-05 pTab[nTab]->PutCell( rPos, nFormatIndex, pCell ); +//UNUSED2009-05 } + +BOOL ScDocument::GetPrintArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow, + BOOL bNotes ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + { + BOOL bAny = pTab[nTab]->GetPrintArea( rEndCol, rEndRow, bNotes ); + if (pDrawLayer) + { + ScRange aDrawRange(0,0,nTab, MAXCOL,MAXROW,nTab); + if (DrawGetPrintArea( aDrawRange, TRUE, TRUE )) + { + if (aDrawRange.aEnd.Col()>rEndCol) rEndCol=aDrawRange.aEnd.Col(); + if (aDrawRange.aEnd.Row()>rEndRow) rEndRow=aDrawRange.aEnd.Row(); + bAny = TRUE; + } + } + return bAny; + } + + rEndCol = 0; + rEndRow = 0; + return FALSE; +} + +BOOL ScDocument::GetPrintAreaHor( SCTAB nTab, SCROW nStartRow, SCROW nEndRow, + SCCOL& rEndCol, BOOL bNotes ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + { + BOOL bAny = pTab[nTab]->GetPrintAreaHor( nStartRow, nEndRow, rEndCol, bNotes ); + if (pDrawLayer) + { + ScRange aDrawRange(0,nStartRow,nTab, MAXCOL,nEndRow,nTab); + if (DrawGetPrintArea( aDrawRange, TRUE, FALSE )) + { + if (aDrawRange.aEnd.Col()>rEndCol) rEndCol=aDrawRange.aEnd.Col(); + bAny = TRUE; + } + } + return bAny; + } + + rEndCol = 0; + return FALSE; +} + +BOOL ScDocument::GetPrintAreaVer( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, + SCROW& rEndRow, BOOL bNotes ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + { + BOOL bAny = pTab[nTab]->GetPrintAreaVer( nStartCol, nEndCol, rEndRow, bNotes ); + if (pDrawLayer) + { + ScRange aDrawRange(nStartCol,0,nTab, nEndCol,MAXROW,nTab); + if (DrawGetPrintArea( aDrawRange, FALSE, TRUE )) + { + if (aDrawRange.aEnd.Row()>rEndRow) rEndRow=aDrawRange.aEnd.Row(); + bAny = TRUE; + } + } + return bAny; + } + + rEndRow = 0; + return FALSE; +} + +BOOL ScDocument::GetDataStart( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + { + BOOL bAny = pTab[nTab]->GetDataStart( rStartCol, rStartRow ); + if (pDrawLayer) + { + ScRange aDrawRange(0,0,nTab, MAXCOL,MAXROW,nTab); + if (DrawGetPrintArea( aDrawRange, TRUE, TRUE )) + { + if (aDrawRange.aStart.Col()<rStartCol) rStartCol=aDrawRange.aStart.Col(); + if (aDrawRange.aStart.Row()<rStartRow) rStartRow=aDrawRange.aStart.Row(); + bAny = TRUE; + } + } + return bAny; + } + + rStartCol = 0; + rStartRow = 0; + return FALSE; +} + +BOOL ScDocument::MoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + if (nOldPos == nNewPos) return FALSE; + BOOL bValid = FALSE; + if (VALIDTAB(nOldPos)) + { + if (pTab[nOldPos]) + { + SCTAB nTabCount = GetTableCount(); + if (nTabCount > 1) + { + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + SetNoListening( TRUE ); + ScProgress* pProgress = new ScProgress( GetDocumentShell(), + ScGlobal::GetRscString(STR_UNDO_MOVE_TAB), GetCodeCount() ); + if (nNewPos == SC_TAB_APPEND) + nNewPos = nTabCount-1; + + // Referenz-Updaterei + //! mit UpdateReference zusammenfassen! + + SCsTAB nDz = ((SCsTAB)nNewPos) - (SCsTAB)nOldPos; + ScRange aSourceRange( 0,0,nOldPos, MAXCOL,MAXROW,nOldPos ); + pRangeName->UpdateTabRef(nOldPos, 3, nNewPos); + pDBCollection->UpdateMoveTab( nOldPos, nNewPos ); + xColNameRanges->UpdateReference( URM_REORDER, this, aSourceRange, 0,0,nDz ); + xRowNameRanges->UpdateReference( URM_REORDER, this, aSourceRange, 0,0,nDz ); + if (pDPCollection) + pDPCollection->UpdateReference( URM_REORDER, aSourceRange, 0,0,nDz ); + if (pDetOpList) + pDetOpList->UpdateReference( this, URM_REORDER, aSourceRange, 0,0,nDz ); + UpdateChartRef( URM_REORDER, + 0,0,nOldPos, MAXCOL,MAXROW,nOldPos, 0,0,nDz ); + UpdateRefAreaLinks( URM_REORDER, aSourceRange, 0,0,nDz ); + if ( pCondFormList ) + pCondFormList->UpdateMoveTab( nOldPos, nNewPos ); + if ( pValidationList ) + pValidationList->UpdateMoveTab( nOldPos, nNewPos ); + if ( pUnoBroadcaster ) + pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_REORDER, + aSourceRange, 0,0,nDz ) ); + + ScTable* pSaveTab = pTab[nOldPos]; + SCTAB i; + for (i = nOldPos + 1; i < nTabCount; i++) + pTab[i - 1] = pTab[i]; + pTab[i-1] = NULL; + for (i = nTabCount - 1; i > nNewPos; i--) + pTab[i] = pTab[i - 1]; + pTab[nNewPos] = pSaveTab; + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateMoveTab( nOldPos, nNewPos, i, *pProgress ); + delete pProgress; // freimachen fuer evtl. andere + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateCompile(); + SetNoListening( FALSE ); + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartAllListeners(); + // #81844# sheet names of references may not be valid until sheet is moved + pChartListenerCollection->UpdateScheduledSeriesRanges(); + SetDirty(); + SetAutoCalc( bOldAutoCalc ); + + if (pDrawLayer) + DrawMovePage( static_cast<sal_uInt16>(nOldPos), static_cast<sal_uInt16>(nNewPos) ); + + // Update cells containing external references. + if (pExternalRefMgr.get()) + pExternalRefMgr->updateRefMoveTable(nOldPos, nNewPos, false); + + bValid = TRUE; + } + } + } + return bValid; +} + +BOOL ScDocument::CopyTab( SCTAB nOldPos, SCTAB nNewPos, const ScMarkData* pOnlyMarked ) +{ + if (SC_TAB_APPEND == nNewPos ) nNewPos = nMaxTableNumber; + String aName; + GetName(nOldPos, aName); + + // vorneweg testen, ob der Prefix als gueltig erkannt wird + // wenn nicht, nur doppelte vermeiden + BOOL bPrefix = ValidTabName( aName ); + DBG_ASSERT(bPrefix, "ungueltiger Tabellenname"); + SCTAB nDummy; + + CreateValidTabName(aName); + + BOOL bValid; + if (bPrefix) + bValid = ( ValidNewTabName(aName) && (nMaxTableNumber <= MAXTAB) ); + else + bValid = ( !GetTable( aName, nDummy ) && (nMaxTableNumber <= MAXTAB) ); + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + if (bValid) + { + if (nNewPos == nMaxTableNumber) + { + pTab[nMaxTableNumber] = new ScTable(this, nMaxTableNumber, aName); + ++nMaxTableNumber; + } + else + { + if (VALIDTAB(nNewPos) && (nNewPos < nMaxTableNumber)) + { + SetNoListening( TRUE ); + + ScRange aRange( 0,0,nNewPos, MAXCOL,MAXROW,MAXTAB ); + xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); + xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); + pRangeName->UpdateTabRef(nNewPos, 1); + pDBCollection->UpdateReference( + URM_INSDEL, 0,0,nNewPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); + if (pDPCollection) + pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + if (pDetOpList) + pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 ); + UpdateChartRef( URM_INSDEL, 0,0,nNewPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); + UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 ); + if ( pUnoBroadcaster ) + pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) ); + + SCTAB i; + for (i = 0; i <= MAXTAB; i++) + if (pTab[i] && i != nOldPos) + pTab[i]->UpdateInsertTab(nNewPos); + for (i = nMaxTableNumber; i > nNewPos; i--) + pTab[i] = pTab[i - 1]; + if (nNewPos <= nOldPos) + nOldPos++; + pTab[nNewPos] = new ScTable(this, nNewPos, aName); + ++nMaxTableNumber; + bValid = TRUE; + for (i = 0; i <= MAXTAB; i++) + if (pTab[i] && i != nOldPos && i != nNewPos) + pTab[i]->UpdateCompile(); + SetNoListening( FALSE ); + for (i = 0; i <= MAXTAB; i++) + if (pTab[i] && i != nOldPos && i != nNewPos) + pTab[i]->StartAllListeners(); + + // update conditional formats after table is inserted + if ( pCondFormList ) + pCondFormList->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + if ( pValidationList ) + pValidationList->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + // #81844# sheet names of references may not be valid until sheet is copied + pChartListenerCollection->UpdateScheduledSeriesRanges(); + } + else + bValid = FALSE; + } + } + if (bValid) + { + SetNoListening( TRUE ); // noch nicht bei CopyToTable/Insert + pTab[nOldPos]->CopyToTable(0, 0, MAXCOL, MAXROW, IDF_ALL, (pOnlyMarked != NULL), + pTab[nNewPos], pOnlyMarked ); + SCsTAB nDz; +/* if (nNewPos < nOldPos) + nDz = ((short)nNewPos) - (short)nOldPos + 1; + else +*/ nDz = ((short)nNewPos) - (short)nOldPos; + pTab[nNewPos]->UpdateReference(URM_COPY, 0, 0, nNewPos , MAXCOL, MAXROW, + nNewPos, 0, 0, nDz, NULL); + + pTab[nNewPos]->UpdateInsertTabAbs(nNewPos); // alle abs. um eins hoch!! + pTab[nOldPos]->UpdateInsertTab(nNewPos); + + pTab[nOldPos]->UpdateCompile(); + pTab[nNewPos]->UpdateCompile( TRUE ); // #67996# maybe already compiled in Clone, but used names need recompilation + SetNoListening( FALSE ); + pTab[nOldPos]->StartAllListeners(); + pTab[nNewPos]->StartAllListeners(); + SetDirty(); + SetAutoCalc( bOldAutoCalc ); + + if (pDrawLayer) + DrawCopyPage( static_cast<sal_uInt16>(nOldPos), static_cast<sal_uInt16>(nNewPos) ); + + pTab[nNewPos]->SetPageStyle( pTab[nOldPos]->GetPageStyle() ); + pTab[nNewPos]->SetPendingRowHeights( pTab[nOldPos]->IsPendingRowHeights() ); + + // Update cells containing external references. + if (pExternalRefMgr.get()) + pExternalRefMgr->updateRefMoveTable(nOldPos, nNewPos, true); + } + else + SetAutoCalc( bOldAutoCalc ); + return bValid; +} + +ULONG ScDocument::TransferTab( ScDocument* pSrcDoc, SCTAB nSrcPos, + SCTAB nDestPos, BOOL bInsertNew, + BOOL bResultsOnly ) +{ + ULONG nRetVal = 1; // 0 => Fehler 1 = ok + // 2 => RefBox, 3 => NameBox + // 4 => beides + BOOL bValid = TRUE; + if (bInsertNew) // neu einfuegen + { + String aName; + pSrcDoc->GetName(nSrcPos, aName); + CreateValidTabName(aName); + bValid = InsertTab(nDestPos, aName); + } + else // bestehende Tabelle ersetzen + { + if (VALIDTAB(nDestPos) && pTab[nDestPos]) + { + pTab[nDestPos]->DeleteArea( 0,0, MAXCOL,MAXROW, IDF_ALL ); + } + else + bValid = FALSE; + } + + if (bValid) + { + BOOL bOldAutoCalcSrc = FALSE; + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + SetNoListening( TRUE ); + if ( bResultsOnly ) + { + bOldAutoCalcSrc = pSrcDoc->GetAutoCalc(); + pSrcDoc->SetAutoCalc( TRUE ); // falls was berechnet werden muss + } + + { + NumFmtMergeHandler aNumFmtMergeHdl(this, pSrcDoc); + + nDestPos = Min(nDestPos, (SCTAB)(GetTableCount() - 1)); + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( pBASM); + pSrcDoc->pTab[nSrcPos]->CopyToTable(0, 0, MAXCOL, MAXROW, + ( bResultsOnly ? IDF_ALL & ~IDF_FORMULA : IDF_ALL), + FALSE, pTab[nDestPos] ); + } + } + pTab[nDestPos]->SetTabNo(nDestPos); + + if ( !bResultsOnly ) + { + BOOL bNamesLost = FALSE; + USHORT nSrcRangeNames = pSrcDoc->pRangeName->GetCount(); + // array containing range names which might need update of indices + ScRangeData** pSrcRangeNames = nSrcRangeNames ? new ScRangeData* [nSrcRangeNames] : NULL; + // the index mapping thereof + ScRangeData::IndexMap aSrcRangeMap; + BOOL bRangeNameReplace = FALSE; + + // find named ranges that are used in the source sheet + std::set<USHORT> aUsedNames; + pSrcDoc->pTab[nSrcPos]->FindRangeNamesInUse( 0, 0, MAXCOL, MAXROW, aUsedNames ); + + for (USHORT i = 0; i < nSrcRangeNames; i++) //! DB-Bereiche Pivot-Bereiche auch !!! + { + ScRangeData* pSrcData = (*pSrcDoc->pRangeName)[i]; + USHORT nOldIndex = pSrcData->GetIndex(); + bool bInUse = ( aUsedNames.find(nOldIndex) != aUsedNames.end() ); + if (bInUse) + { + USHORT nExisting = 0; + if ( pRangeName->SearchName( pSrcData->GetName(), nExisting ) ) + { + // the name exists already in the destination document + // -> use the existing name, but show a warning + // (when refreshing links, the existing name is used and the warning not shown) + + ScRangeData* pExistingData = (*pRangeName)[nExisting]; + USHORT nExistingIndex = pExistingData->GetIndex(); + + pSrcRangeNames[i] = NULL; // don't modify the named range + aSrcRangeMap.insert( + ScRangeData::IndexMap::value_type(nOldIndex, nExistingIndex)); + bRangeNameReplace = TRUE; + bNamesLost = TRUE; + } + else + { + ScRangeData* pData = new ScRangeData( *pSrcData ); + pData->SetDocument(this); + if ( pRangeName->FindIndex( pData->GetIndex() ) ) + pData->SetIndex(0); // need new index, done in Insert + if (!pRangeName->Insert(pData)) + { + DBG_ERROR("can't insert name"); // shouldn't happen + delete pData; + } + else + { + pData->TransferTabRef( nSrcPos, nDestPos ); + pSrcRangeNames[i] = pData; + USHORT nNewIndex = pData->GetIndex(); + aSrcRangeMap.insert( + ScRangeData::IndexMap::value_type(nOldIndex, nNewIndex)); + if ( !bRangeNameReplace ) + bRangeNameReplace = ( nOldIndex != nNewIndex ); + } + } + } + else + { + pSrcRangeNames[i] = NULL; + //aSrcRangeMap.SetPair( i, 0, 0 ); // not needed, defaulted + } + } + if ( bRangeNameReplace ) + { + // first update all inserted named formulas if they contain other + // range names and used indices changed + for (USHORT i = 0; i < nSrcRangeNames; i++) //! DB-Bereiche Pivot-Bereiche auch + { + if ( pSrcRangeNames[i] ) + pSrcRangeNames[i]->ReplaceRangeNamesInUse( aSrcRangeMap ); + } + // then update the formulas, they might need the just updated range names + pTab[nDestPos]->ReplaceRangeNamesInUse( 0, 0, MAXCOL, MAXROW, aSrcRangeMap ); + } + if ( pSrcRangeNames ) + delete [] pSrcRangeNames; + + SCsTAB nDz = ((SCsTAB)nDestPos) - (SCsTAB)nSrcPos; + pTab[nDestPos]->UpdateReference(URM_COPY, 0, 0, nDestPos, + MAXCOL, MAXROW, nDestPos, + 0, 0, nDz, NULL); + // Test for outside absolute references for info box + BOOL bIsAbsRef = pSrcDoc->pTab[nSrcPos]->TestTabRefAbs(nSrcPos); + // Readjust self-contained absolute references to this sheet + pTab[nDestPos]->TestTabRefAbs(nSrcPos); + if (bIsAbsRef) + { + nRetVal += 1; + // InfoBox AbsoluteRefs sind moeglicherweise nicht mehr korrekt!! + } + if (bNamesLost) + { + nRetVal += 2; + // message: duplicate names + } + pTab[nDestPos]->CompileAll(); + } + + SetNoListening( FALSE ); + if ( !bResultsOnly ) + pTab[nDestPos]->StartAllListeners(); + SetDirty( ScRange( 0, 0, nDestPos, MAXCOL, MAXROW, nDestPos)); + + if ( bResultsOnly ) + pSrcDoc->SetAutoCalc( bOldAutoCalcSrc ); + SetAutoCalc( bOldAutoCalc ); + + // Drawing kopieren + + if (bInsertNew) + TransferDrawPage( pSrcDoc, nSrcPos, nDestPos ); + + pTab[nDestPos]->SetPendingRowHeights( pSrcDoc->pTab[nSrcPos]->IsPendingRowHeights() ); + } + if (!bValid) + nRetVal = 0; + return nRetVal; +} + +// ---------------------------------------------------------------------------- + +void ScDocument::SetError( SCCOL nCol, SCROW nRow, SCTAB nTab, const USHORT nError) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->SetError( nCol, nRow, nError ); +} + +void ScDocument::EraseNonUsedSharedNames(USHORT nLevel) +{ + for (USHORT i = 0; i < pRangeName->GetCount(); i++) + { + ScRangeData* pRangeData = (*pRangeName)[i]; + if (pRangeData && pRangeData->HasType(RT_SHARED)) + { + String aName; + pRangeData->GetName(aName); + aName.Erase(0, 6); // !!! vgl. Table4, FillFormula !! + USHORT nInd = (USHORT) aName.ToInt32(); + if (nInd <= nLevel) + { + USHORT nIndex = pRangeData->GetIndex(); + BOOL bInUse = FALSE; + for (SCTAB j = 0; !bInUse && (j <= MAXTAB); j++) + { + if (pTab[j]) + bInUse = pTab[j]->IsRangeNameInUse(0, 0, MAXCOL-1, MAXROW-1, + nIndex); + } + if (!bInUse) + pRangeName->AtFree(i); + } + } + } +} + +// ---------------------------------------------------------------------------- + +void ScDocument::SetConsolidateDlgData( const ScConsolidateParam* pData ) +{ + delete pConsolidateDlgData; + + if ( pData ) + pConsolidateDlgData = new ScConsolidateParam( *pData ); + else + pConsolidateDlgData = NULL; +} + +void ScDocument::SetChangeViewSettings(const ScChangeViewSettings& rNew) +{ + if (pChangeViewSettings==NULL) + pChangeViewSettings = new ScChangeViewSettings; + + DBG_ASSERT( pChangeViewSettings, "Oops. No ChangeViewSettings :-( by!" ); + + *pChangeViewSettings=rNew; +} + +// ---------------------------------------------------------------------------- + +ScFieldEditEngine* ScDocument::CreateFieldEditEngine() +{ + ScFieldEditEngine* pNewEditEngine = NULL; + if (!pCacheFieldEditEngine) + { + pNewEditEngine = new ScFieldEditEngine( GetEnginePool(), + GetEditPool(), FALSE ); + } + else + { + if ( !bImportingXML ) + { + // #i66209# previous use might not have restored update mode, + // ensure same state as for a new EditEngine (UpdateMode = TRUE) + if ( !pCacheFieldEditEngine->GetUpdateMode() ) + pCacheFieldEditEngine->SetUpdateMode(TRUE); + } + + pNewEditEngine = pCacheFieldEditEngine; + pCacheFieldEditEngine = NULL; + } + return pNewEditEngine; +} + +void ScDocument::DisposeFieldEditEngine(ScFieldEditEngine*& rpEditEngine) +{ + if (!pCacheFieldEditEngine && rpEditEngine) + { + pCacheFieldEditEngine = rpEditEngine; + pCacheFieldEditEngine->Clear(); + } + else + delete rpEditEngine; + rpEditEngine = NULL; +} + +// ---------------------------------------------------------------------------- + +// static +ScRecursionHelper* ScDocument::CreateRecursionHelperInstance() +{ + return new ScRecursionHelper; +} + +// ---------------------------------------------------------------------------- + +ScLookupCache & ScDocument::GetLookupCache( const ScRange & rRange ) +{ + ScLookupCache* pCache = 0; + if (!pLookupCacheMapImpl) + pLookupCacheMapImpl = new ScLookupCacheMapImpl; + ScLookupCacheMap::iterator it( pLookupCacheMapImpl->aCacheMap.find( rRange)); + if (it == pLookupCacheMapImpl->aCacheMap.end()) + { + pCache = new ScLookupCache( this, rRange); + AddLookupCache( *pCache); + } + else + pCache = (*it).second; + return *pCache; +} + +void ScDocument::AddLookupCache( ScLookupCache & rCache ) +{ + if (!pLookupCacheMapImpl->aCacheMap.insert( ::std::pair< const ScRange, + ScLookupCache*>( rCache.getRange(), &rCache)).second) + { + DBG_ERRORFILE( "ScDocument::AddLookupCache: couldn't add to hash map"); + } + else + StartListeningArea( rCache.getRange(), &rCache); +} + +void ScDocument::RemoveLookupCache( ScLookupCache & rCache ) +{ + ScLookupCacheMap::iterator it( pLookupCacheMapImpl->aCacheMap.find( + rCache.getRange())); + if (it == pLookupCacheMapImpl->aCacheMap.end()) + { + DBG_ERRORFILE( "ScDocument::RemoveLookupCache: range not found in hash map"); + } + else + { + ScLookupCache* pCache = (*it).second; + pLookupCacheMapImpl->aCacheMap.erase( it); + EndListeningArea( pCache->getRange(), &rCache); + } +} + +void ScDocument::ClearLookupCaches() +{ + if( pLookupCacheMapImpl ) + pLookupCacheMapImpl->clear(); +} diff --git a/sc/source/core/data/documen3.cxx b/sc/source/core/data/documen3.cxx new file mode 100644 index 000000000000..17650028db70 --- /dev/null +++ b/sc/source/core/data/documen3.cxx @@ -0,0 +1,1909 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen3.cxx,v $ + * $Revision: 1.42.100.5 $ + * + * 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 "scitems.hxx" +#include <svx/langitem.hxx> +#include <svx/srchitem.hxx> +#include <svx/linkmgr.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/objsh.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/PasswordHelper.hxx> +#include <vcl/svapp.hxx> +#include "document.hxx" +#include "attrib.hxx" +#include "cell.hxx" +#include "table.hxx" +#include "rangenam.hxx" +#include "dbcolect.hxx" +#include "pivot.hxx" +#include "docpool.hxx" +#include "poolhelp.hxx" +#include "autoform.hxx" +#include "rangelst.hxx" +#include "chartarr.hxx" +#include "chartlock.hxx" +#include "refupdat.hxx" +#include "docoptio.hxx" +#include "viewopti.hxx" +#include "scextopt.hxx" +#include "brdcst.hxx" +#include "bcaslot.hxx" +#include "tablink.hxx" +#include "externalrefmgr.hxx" +#include "markdata.hxx" +#include "validat.hxx" +#include "dociter.hxx" +#include "detdata.hxx" +#include "detfunc.hxx" +#include "scmod.hxx" // SC_MOD +#include "inputopt.hxx" // GetExpandRefs +#include "chartlis.hxx" +#include "sc.hrc" // SID_LINK +#include "hints.hxx" +#include "dpobject.hxx" +#include "unoguard.hxx" +#include "drwlayer.hxx" +#include "unoreflist.hxx" +#include "listenercalls.hxx" +#include "tabprotection.hxx" +#include "formulaparserpool.hxx" +#include "clipparam.hxx" + +#include <memory> + +using namespace com::sun::star; +using ::std::auto_ptr; + +//------------------------------------------------------------------------ + +ScRangeName* ScDocument::GetRangeName() +{ + return pRangeName; +} + +void ScDocument::SetRangeName( ScRangeName* pNewRangeName ) +{ + if (pRangeName) + delete pRangeName; + pRangeName = pNewRangeName; +} + +//UNUSED2008-05 ScRangeData* ScDocument::GetRangeAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, +//UNUSED2008-05 BOOL bStartOnly) const +//UNUSED2008-05 { +//UNUSED2008-05 if ( pRangeName ) +//UNUSED2008-05 return pRangeName->GetRangeAtCursor( ScAddress( nCol, nRow, nTab ), bStartOnly ); +//UNUSED2008-05 else +//UNUSED2008-05 return NULL; +//UNUSED2008-05 } + +ScRangeData* ScDocument::GetRangeAtBlock( const ScRange& rBlock, String* pName ) const +{ + ScRangeData* pData = NULL; + if ( pRangeName ) + { + pData = pRangeName->GetRangeAtBlock( rBlock ); + if (pData && pName) + *pName = pData->GetName(); + } + return pData; +} + +ScDBCollection* ScDocument::GetDBCollection() const +{ + return pDBCollection; +} + +void ScDocument::SetDBCollection( ScDBCollection* pNewDBCollection, BOOL bRemoveAutoFilter ) +{ + if ( bRemoveAutoFilter ) + { + // remove auto filter attribute if new db data don't contain auto filter flag + // start position is also compared, so bRemoveAutoFilter must not be set from ref-undo! + + if ( pDBCollection ) + { + USHORT nOldCount = pDBCollection->GetCount(); + for (USHORT nOld=0; nOld<nOldCount; nOld++) + { + ScDBData* pOldData = (*pDBCollection)[nOld]; + if ( pOldData->HasAutoFilter() ) + { + ScRange aOldRange; + pOldData->GetArea( aOldRange ); + + BOOL bFound = FALSE; + USHORT nNewIndex = 0; + if ( pNewDBCollection && + pNewDBCollection->SearchName( pOldData->GetName(), nNewIndex ) ) + { + ScDBData* pNewData = (*pNewDBCollection)[nNewIndex]; + if ( pNewData->HasAutoFilter() ) + { + ScRange aNewRange; + pNewData->GetArea( aNewRange ); + if ( aOldRange.aStart == aNewRange.aStart ) + bFound = TRUE; + } + } + + if ( !bFound ) + { + aOldRange.aEnd.SetRow( aOldRange.aStart.Row() ); + RemoveFlagsTab( aOldRange.aStart.Col(), aOldRange.aStart.Row(), + aOldRange.aEnd.Col(), aOldRange.aEnd.Row(), + aOldRange.aStart.Tab(), SC_MF_AUTO ); + if (pShell) + pShell->Broadcast( ScPaintHint( aOldRange, PAINT_GRID ) ); + } + } + } + } + } + + if (pDBCollection) + delete pDBCollection; + pDBCollection = pNewDBCollection; +} + +ScDBData* ScDocument::GetDBAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab, BOOL bStartOnly) const +{ + if (pDBCollection) + return pDBCollection->GetDBAtCursor(nCol, nRow, nTab, bStartOnly); + else + return NULL; +} + +ScDBData* ScDocument::GetDBAtArea(SCTAB nTab, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2) const +{ + if (pDBCollection) + return pDBCollection->GetDBAtArea(nTab, nCol1, nRow1, nCol2, nRow2); + else + return NULL; +} + +ScDPCollection* ScDocument::GetDPCollection() +{ + if (!pDPCollection) + pDPCollection = new ScDPCollection(this); + return pDPCollection; +} + +ScDPObject* ScDocument::GetDPAtCursor(SCCOL nCol, SCROW nRow, SCTAB nTab) const +{ + if (!pDPCollection) + return NULL; + + USHORT nCount = pDPCollection->GetCount(); + ScAddress aPos( nCol, nRow, nTab ); + for (USHORT i=0; i<nCount; i++) + if ( (*pDPCollection)[i]->GetOutRange().In( aPos ) ) + return (*pDPCollection)[i]; + + return NULL; +} + +ScDPObject* ScDocument::GetDPAtBlock( const ScRange & rBlock ) const +{ + if (!pDPCollection) + return NULL; + + /* Walk the collection in reverse order to get something of an + * approximation of MS Excels 'most recent' effect. */ + USHORT i = pDPCollection->GetCount(); + while ( i-- > 0 ) + if ( (*pDPCollection)[i]->GetOutRange().In( rBlock ) ) + return (*pDPCollection)[i]; + + return NULL; +} + +ScChartCollection* ScDocument::GetChartCollection() const +{ + return pChartCollection; +} + +void ScDocument::StopTemporaryChartLock() +{ + if( apTemporaryChartLock.get() ) + apTemporaryChartLock->StopLocking(); +} + +void ScDocument::SetChartListenerCollection( + ScChartListenerCollection* pNewChartListenerCollection, + BOOL bSetChartRangeLists ) +{ + ScChartListenerCollection* pOld = pChartListenerCollection; + pChartListenerCollection = pNewChartListenerCollection; + if ( pChartListenerCollection ) + { + if ( pOld ) + pChartListenerCollection->SetDiffDirty( *pOld, bSetChartRangeLists ); + pChartListenerCollection->StartAllListeners(); + } + delete pOld; +} + +void ScDocument::SetScenario( SCTAB nTab, BOOL bFlag ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetScenario(bFlag); +} + +BOOL ScDocument::IsScenario( SCTAB nTab ) const +{ + return ValidTab(nTab) && pTab[nTab] &&pTab[nTab]->IsScenario(); + //if (ValidTab(nTab) && pTab[nTab]) + // return pTab[nTab]->IsScenario(); + + //return FALSE; +} + +void ScDocument::SetScenarioData( SCTAB nTab, const String& rComment, + const Color& rColor, USHORT nFlags ) +{ + if (ValidTab(nTab) && pTab[nTab] && pTab[nTab]->IsScenario()) + { + pTab[nTab]->SetScenarioComment( rComment ); + pTab[nTab]->SetScenarioColor( rColor ); + pTab[nTab]->SetScenarioFlags( nFlags ); + } +} + +void ScDocument::GetScenarioData( SCTAB nTab, String& rComment, + Color& rColor, USHORT& rFlags ) const +{ + if (ValidTab(nTab) && pTab[nTab] && pTab[nTab]->IsScenario()) + { + pTab[nTab]->GetScenarioComment( rComment ); + rColor = pTab[nTab]->GetScenarioColor(); + rFlags = pTab[nTab]->GetScenarioFlags(); + } +} + +void ScDocument::GetScenarioFlags( SCTAB nTab, USHORT& rFlags ) const +{ + if (VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->IsScenario()) + rFlags = pTab[nTab]->GetScenarioFlags(); +} + +BOOL ScDocument::IsLinked( SCTAB nTab ) const +{ + return ValidTab(nTab) && pTab[nTab] && pTab[nTab]->IsLinked(); + // euqivalent to + //if (ValidTab(nTab) && pTab[nTab]) + // return pTab[nTab]->IsLinked(); + //return FALSE; +} + +formula::FormulaGrammar::AddressConvention ScDocument::GetAddressConvention() const +{ + return formula::FormulaGrammar::extractRefConvention(eGrammar); +} + +formula::FormulaGrammar::Grammar ScDocument::GetGrammar() const +{ + return eGrammar; +} + +void ScDocument::SetGrammar( formula::FormulaGrammar::Grammar eGram ) +{ + eGrammar = eGram; +} + +BOOL ScDocument::GetLinkMode( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkMode(); + return SC_LINK_NONE; +} + +const String& ScDocument::GetLinkDoc( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkDoc(); + return EMPTY_STRING; +} + +const String& ScDocument::GetLinkFlt( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkFlt(); + return EMPTY_STRING; +} + +const String& ScDocument::GetLinkOpt( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkOpt(); + return EMPTY_STRING; +} + +const String& ScDocument::GetLinkTab( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkTab(); + return EMPTY_STRING; +} + +ULONG ScDocument::GetLinkRefreshDelay( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetLinkRefreshDelay(); + return 0; +} + +void ScDocument::SetLink( SCTAB nTab, BYTE nMode, const String& rDoc, + const String& rFilter, const String& rOptions, + const String& rTabName, ULONG nRefreshDelay ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetLink( nMode, rDoc, rFilter, rOptions, rTabName, nRefreshDelay ); +} + +BOOL ScDocument::HasLink( const String& rDoc, + const String& rFilter, const String& rOptions ) const +{ + SCTAB nCount = GetTableCount(); + for (SCTAB i=0; i<nCount; i++) + if (pTab[i]->IsLinked() + && pTab[i]->GetLinkDoc() == rDoc + && pTab[i]->GetLinkFlt() == rFilter + && pTab[i]->GetLinkOpt() == rOptions) + return TRUE; + + return FALSE; +} + +BOOL ScDocument::LinkExternalTab( SCTAB& rTab, const String& aDocTab, + const String& aFileName, const String& aTabName ) +{ + if ( IsClipboard() ) + { + DBG_ERRORFILE( "LinkExternalTab in Clipboard" ); + return FALSE; + } + rTab = 0; + String aFilterName; // wird vom Loader gefuellt + String aOptions; // Filter-Optionen + sal_uInt32 nLinkCnt = pExtDocOptions ? pExtDocOptions->GetDocSettings().mnLinkCnt : 0; + ScDocumentLoader aLoader( aFileName, aFilterName, aOptions, nLinkCnt + 1 ); + if ( aLoader.IsError() ) + return FALSE; + ScDocument* pSrcDoc = aLoader.GetDocument(); + + // Tabelle kopieren + SCTAB nSrcTab; + if ( pSrcDoc->GetTable( aTabName, nSrcTab ) ) + { + if ( !InsertTab( SC_TAB_APPEND, aDocTab, TRUE ) ) + { + DBG_ERRORFILE("can't insert external document table"); + return FALSE; + } + rTab = GetTableCount() - 1; + // nicht neu einfuegen, nur Ergebnisse + TransferTab( pSrcDoc, nSrcTab, rTab, FALSE, TRUE ); + } + else + return FALSE; + + ULONG nRefreshDelay = 0; + + BOOL bWasThere = HasLink( aFileName, aFilterName, aOptions ); + SetLink( rTab, SC_LINK_VALUE, aFileName, aFilterName, aOptions, aTabName, nRefreshDelay ); + if ( !bWasThere ) // Link pro Quelldokument nur einmal eintragen + { + ScTableLink* pLink = new ScTableLink( pShell, aFileName, aFilterName, aOptions, nRefreshDelay ); + pLink->SetInCreate( TRUE ); + GetLinkManager()->InsertFileLink( *pLink, OBJECT_CLIENT_FILE, aFileName, + &aFilterName ); + pLink->Update(); + pLink->SetInCreate( FALSE ); + SfxBindings* pBindings = GetViewBindings(); + if (pBindings) + pBindings->Invalidate( SID_LINKS ); + } + return TRUE; +} + +ScExternalRefManager* ScDocument::GetExternalRefManager() const +{ + ScDocument* pThis = const_cast<ScDocument*>(this); + if (!pExternalRefMgr.get()) + pThis->pExternalRefMgr.reset( new ScExternalRefManager( pThis)); + + return pExternalRefMgr.get(); +} + +bool ScDocument::IsInExternalReferenceMarking() const +{ + return pExternalRefMgr.get() && pExternalRefMgr->isInReferenceMarking(); +} + +void ScDocument::MarkUsedExternalReferences() +{ + if (!pExternalRefMgr.get()) + return; + if (!pExternalRefMgr->hasExternalData()) + return; + // Charts. + bool bAllMarked = pExternalRefMgr->markUsedByLinkListeners(); + // Formula cells. + for (SCTAB nTab = 0; !bAllMarked && nTab < nMaxTableNumber; ++nTab) + { + if (pTab[nTab]) + bAllMarked = pTab[nTab]->MarkUsedExternalReferences(); + } + /* NOTE: Conditional formats and validation objects are marked when + * collecting them during export. */ +} + +ScFormulaParserPool& ScDocument::GetFormulaParserPool() const +{ + if( !mxFormulaParserPool.get() ) + mxFormulaParserPool.reset( new ScFormulaParserPool( *this ) ); + return *mxFormulaParserPool; +} + +ScOutlineTable* ScDocument::GetOutlineTable( SCTAB nTab, BOOL bCreate ) +{ + ScOutlineTable* pVal = NULL; + + if (VALIDTAB(nTab)) + if (pTab[nTab]) + { + pVal = pTab[nTab]->GetOutlineTable(); + if (!pVal) + if (bCreate) + { + pTab[nTab]->StartOutlineTable(); + pVal = pTab[nTab]->GetOutlineTable(); + } + } + + return pVal; +} + +BOOL ScDocument::SetOutlineTable( SCTAB nTab, const ScOutlineTable* pNewOutline ) +{ + return VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->SetOutlineTable(pNewOutline); + //if (VALIDTAB(nTab)) + // if (pTab[nTab]) + // return pTab[nTab]->SetOutlineTable(pNewOutline); + + //return FALSE; +} + +void ScDocument::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) +{ + if (VALIDTAB(nTab) && pTab[nTab]) + pTab[nTab]->DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow ); +} + +BOOL ScDocument::TestRemoveSubTotals( SCTAB nTab, const ScSubTotalParam& rParam ) +{ + return VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->TestRemoveSubTotals( rParam ); + //if (VALIDTAB(nTab) && pTab[nTab] ) + // return pTab[nTab]->TestRemoveSubTotals( rParam ); + + //return FALSE; +} + +void ScDocument::RemoveSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + pTab[nTab]->RemoveSubTotals( rParam ); +} + +BOOL ScDocument::DoSubTotals( SCTAB nTab, ScSubTotalParam& rParam ) +{ + return VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->DoSubTotals( rParam ); + //if (VALIDTAB(nTab)) + // if (pTab[nTab]) + // return pTab[nTab]->DoSubTotals( rParam ); + + //return FALSE; +} + +BOOL ScDocument::HasSubTotalCells( const ScRange& rRange ) +{ + ScCellIterator aIter( this, rRange ); + ScBaseCell* pCell = aIter.GetFirst(); + while (pCell) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell)->IsSubTotal() ) + return TRUE; + + pCell = aIter.GetNext(); + } + return FALSE; // none found +} + +// kopiert aus diesem Dokument die Zellen von Positionen, an denen in pPosDoc +// auch Zellen stehen, nach pDestDoc + +void ScDocument::CopyUpdated( ScDocument* pPosDoc, ScDocument* pDestDoc ) +{ + SCTAB nCount = GetTableCount(); + for (SCTAB nTab=0; nTab<nCount; nTab++) + if (pTab[nTab] && pPosDoc->pTab[nTab] && pDestDoc->pTab[nTab]) + pTab[nTab]->CopyUpdated( pPosDoc->pTab[nTab], pDestDoc->pTab[nTab] ); +} + +void ScDocument::CopyScenario( SCTAB nSrcTab, SCTAB nDestTab, BOOL bNewScenario ) +{ + if (ValidTab(nSrcTab) && ValidTab(nDestTab) && pTab[nSrcTab] && pTab[nDestTab]) + { + // Flags fuer aktive Szenarios richtig setzen + // und aktuelle Werte in bisher aktive Szenarios zurueckschreiben + + ScRangeList aRanges = *pTab[nSrcTab]->GetScenarioRanges(); + const ULONG nRangeCount = aRanges.Count(); + + // nDestTab ist die Zieltabelle + for ( SCTAB nTab = nDestTab+1; + nTab<=MAXTAB && pTab[nTab] && pTab[nTab]->IsScenario(); + nTab++ ) + { + if ( pTab[nTab]->IsActiveScenario() ) // auch wenn's dasselbe Szenario ist + { + BOOL bTouched = FALSE; + for ( ULONG nR=0; nR<nRangeCount && !bTouched; nR++) + { + const ScRange* pRange = aRanges.GetObject(nR); + if ( pTab[nTab]->HasScenarioRange( *pRange ) ) + bTouched = TRUE; + } + if (bTouched) + { + pTab[nTab]->SetActiveScenario(FALSE); + if ( pTab[nTab]->GetScenarioFlags() & SC_SCENARIO_TWOWAY ) + pTab[nTab]->CopyScenarioFrom( pTab[nDestTab] ); + } + } + } + + pTab[nSrcTab]->SetActiveScenario(TRUE); // da kommt's her... + if (!bNewScenario) // Daten aus dem ausgewaehlten Szenario kopieren + { + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + pTab[nSrcTab]->CopyScenarioTo( pTab[nDestTab] ); + SetDirty(); + SetAutoCalc( bOldAutoCalc ); + } + } +} + +void ScDocument::MarkScenario( SCTAB nSrcTab, SCTAB nDestTab, ScMarkData& rDestMark, + BOOL bResetMark, USHORT nNeededBits ) const +{ + if (bResetMark) + rDestMark.ResetMark(); + + if (ValidTab(nSrcTab) && pTab[nSrcTab]) + pTab[nSrcTab]->MarkScenarioIn( rDestMark, nNeededBits ); + + rDestMark.SetAreaTab( nDestTab ); +} + +BOOL ScDocument::HasScenarioRange( SCTAB nTab, const ScRange& rRange ) const +{ + return ValidTab(nTab) && pTab[nTab] && pTab[nTab]->HasScenarioRange( rRange ); + //if (ValidTab(nTab) && pTab[nTab]) + // return pTab[nTab]->HasScenarioRange( rRange ); + + //return FALSE; +} + +const ScRangeList* ScDocument::GetScenarioRanges( SCTAB nTab ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetScenarioRanges(); + + return NULL; +} + +BOOL ScDocument::IsActiveScenario( SCTAB nTab ) const +{ + return ValidTab(nTab) && pTab[nTab] && pTab[nTab]->IsActiveScenario( ); + //if (ValidTab(nTab) && pTab[nTab]) + // return pTab[nTab]->IsActiveScenario(); + + //return FALSE; +} + +void ScDocument::SetActiveScenario( SCTAB nTab, BOOL bActive ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetActiveScenario( bActive ); +} + +BOOL ScDocument::TestCopyScenario( SCTAB nSrcTab, SCTAB nDestTab ) const +{ + if (ValidTab(nSrcTab) && ValidTab(nDestTab)) + return pTab[nSrcTab]->TestCopyScenarioTo( pTab[nDestTab] ); + + DBG_ERROR("falsche Tabelle bei TestCopyScenario"); + return FALSE; +} + +void ScDocument::AddUnoObject( SfxListener& rObject ) +{ + if (!pUnoBroadcaster) + pUnoBroadcaster = new SfxBroadcaster; + + rObject.StartListening( *pUnoBroadcaster ); +} + +void ScDocument::RemoveUnoObject( SfxListener& rObject ) +{ + if (pUnoBroadcaster) + { + rObject.EndListening( *pUnoBroadcaster ); + + if ( bInUnoBroadcast ) + { + // #107294# Broadcasts from ScDocument::BroadcastUno are the only way that + // uno object methods are called without holding a reference. + // + // If RemoveUnoObject is called from an object dtor in the finalizer thread + // while the main thread is calling BroadcastUno, the dtor thread must wait + // (or the object's Notify might try to access a deleted object). + // The SolarMutex can't be locked here because if a component is called from + // a VCL event, the main thread has the SolarMutex locked all the time. + // + // This check is done after calling EndListening, so a later BroadcastUno call + // won't touch this object. + + vos::IMutex& rSolarMutex = Application::GetSolarMutex(); + if ( rSolarMutex.tryToAcquire() ) + { + // BroadcastUno is always called with the SolarMutex locked, so if it + // can be acquired, this is within the same thread (should not happen) + DBG_ERRORFILE( "RemoveUnoObject called from BroadcastUno" ); + rSolarMutex.release(); + } + else + { + // let the thread that called BroadcastUno continue + while ( bInUnoBroadcast ) + { + vos::OThread::yield(); + } + } + } + } + else + { + DBG_ERROR("No Uno broadcaster"); + } +} + +void ScDocument::BroadcastUno( const SfxHint &rHint ) +{ + if (pUnoBroadcaster) + { + bInUnoBroadcast = TRUE; + pUnoBroadcaster->Broadcast( rHint ); + bInUnoBroadcast = FALSE; + + // During Broadcast notification, Uno objects can add to pUnoListenerCalls. + // The listener calls must be processed after completing the broadcast, + // because they can add or remove objects from pUnoBroadcaster. + + if ( pUnoListenerCalls && rHint.ISA( SfxSimpleHint ) && + ((const SfxSimpleHint&)rHint).GetId() == SFX_HINT_DATACHANGED && + !bInUnoListenerCall ) + { + // Listener calls may lead to BroadcastUno calls again. The listener calls + // are not nested, instead the calls are collected in the list, and the + // outermost call executes them all. + + ScChartLockGuard aChartLockGuard(this); + bInUnoListenerCall = TRUE; + pUnoListenerCalls->ExecuteAndClear(); + bInUnoListenerCall = FALSE; + } + } +} + +void ScDocument::AddUnoListenerCall( const uno::Reference<util::XModifyListener>& rListener, + const lang::EventObject& rEvent ) +{ + DBG_ASSERT( bInUnoBroadcast, "AddUnoListenerCall is supposed to be called from BroadcastUno only" ); + + if ( !pUnoListenerCalls ) + pUnoListenerCalls = new ScUnoListenerCalls; + pUnoListenerCalls->Add( rListener, rEvent ); +} + +void ScDocument::BeginUnoRefUndo() +{ + DBG_ASSERT( !pUnoRefUndoList, "BeginUnoRefUndo twice" ); + delete pUnoRefUndoList; + + pUnoRefUndoList = new ScUnoRefList; +} + +ScUnoRefList* ScDocument::EndUnoRefUndo() +{ + ScUnoRefList* pRet = pUnoRefUndoList; + pUnoRefUndoList = NULL; + return pRet; // must be deleted by caller! +} + +void ScDocument::AddUnoRefChange( sal_Int64 nId, const ScRangeList& rOldRanges ) +{ + if ( pUnoRefUndoList ) + pUnoRefUndoList->Add( nId, rOldRanges ); +} + +sal_Int64 ScDocument::GetNewUnoId() +{ + return ++nUnoObjectId; +} + +void ScDocument::UpdateReference( UpdateRefMode eUpdateRefMode, + SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScDocument* pUndoDoc, BOOL bIncludeDraw ) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + PutInOrder( nTab1, nTab2 ); + if (VALIDTAB(nTab1) && VALIDTAB(nTab2)) + { + BOOL bExpandRefsOld = IsExpandRefs(); + if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) ) + SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); + SCTAB i; + SCTAB iMax; + if ( eUpdateRefMode == URM_COPY ) + { + i = nTab1; + iMax = nTab2; + } + else + { + ScRange aRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + xColNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz ); + xRowNameRanges->UpdateReference( eUpdateRefMode, this, aRange, nDx, nDy, nDz ); + pDBCollection->UpdateReference( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz ); + pRangeName->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz ); + if ( pDPCollection ) + pDPCollection->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz ); + UpdateChartRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz ); + UpdateRefAreaLinks( eUpdateRefMode, aRange, nDx, nDy, nDz ); + if ( pCondFormList ) + pCondFormList->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz ); + if ( pValidationList ) + pValidationList->UpdateReference( eUpdateRefMode, aRange, nDx, nDy, nDz ); + if ( pDetOpList ) + pDetOpList->UpdateReference( this, eUpdateRefMode, aRange, nDx, nDy, nDz ); + if ( pUnoBroadcaster ) + pUnoBroadcaster->Broadcast( ScUpdateRefHint( + eUpdateRefMode, aRange, nDx, nDy, nDz ) ); + i = 0; + iMax = MAXTAB; + } + for ( ; i<=iMax; i++) + if (pTab[i]) + pTab[i]->UpdateReference( + eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, + nDx, nDy, nDz, pUndoDoc, bIncludeDraw ); + + if ( bIsEmbedded ) + { + SCCOL theCol1; + SCROW theRow1; + SCTAB theTab1; + SCCOL theCol2; + SCROW theRow2; + SCTAB theTab2; + theCol1 = aEmbedRange.aStart.Col(); + theRow1 = aEmbedRange.aStart.Row(); + theTab1 = aEmbedRange.aStart.Tab(); + theCol2 = aEmbedRange.aEnd.Col(); + theRow2 = aEmbedRange.aEnd.Row(); + theTab2 = aEmbedRange.aEnd.Tab(); + if ( ScRefUpdate::Update( this, eUpdateRefMode, nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, + nDx,nDy,nDz, theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ) ) + { + aEmbedRange = ScRange( theCol1,theRow1,theTab1, theCol2,theRow2,theTab2 ); + } + } + SetExpandRefs( bExpandRefsOld ); + + // #30428# after moving, no clipboard move ref-updates are possible + if ( eUpdateRefMode != URM_COPY && IsClipboardSource() ) + { + ScDocument* pClipDoc = SC_MOD()->GetClipDoc(); + if (pClipDoc) + pClipDoc->GetClipParam().mbCutMode = false; + } + } +} + +void ScDocument::UpdateTranspose( const ScAddress& rDestPos, ScDocument* pClipDoc, + const ScMarkData& rMark, ScDocument* pUndoDoc ) +{ + DBG_ASSERT(pClipDoc->bIsClip, "UpdateTranspose: kein Clip"); + + ScRange aSource; + ScClipParam& rClipParam = GetClipParam(); + if (rClipParam.maRanges.Count()) + aSource = *rClipParam.maRanges.First(); + ScAddress aDest = rDestPos; + + SCTAB nClipTab = 0; + for (SCTAB nDestTab=0; nDestTab<=MAXTAB && pTab[nDestTab]; nDestTab++) + if (rMark.GetTableSelect(nDestTab)) + { + while (!pClipDoc->pTab[nClipTab]) nClipTab = (nClipTab+1) % (MAXTAB+1); + aSource.aStart.SetTab( nClipTab ); + aSource.aEnd.SetTab( nClipTab ); + aDest.SetTab( nDestTab ); + + // wie UpdateReference + + pRangeName->UpdateTranspose( aSource, aDest ); // vor den Zellen! + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateTranspose( aSource, aDest, pUndoDoc ); + + nClipTab = (nClipTab+1) % (MAXTAB+1); + } +} + +void ScDocument::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + //! pDBCollection + //! pPivotCollection + //! UpdateChartRef + + pRangeName->UpdateGrow( rArea, nGrowX, nGrowY ); + + for (SCTAB i=0; i<=MAXTAB && pTab[i]; i++) + pTab[i]->UpdateGrow( rArea, nGrowX, nGrowY ); +} + +void ScDocument::Fill(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark, + ULONG nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd, + double nStepValue, double nMaxValue) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->Fill(nCol1, nRow1, nCol2, nRow2, + nFillCount, eFillDir, eFillCmd, eFillDateCmd, + nStepValue, nMaxValue); +} + +String ScDocument::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY ) +{ + SCTAB nTab = rSource.aStart.Tab(); + if (pTab[nTab]) + return pTab[nTab]->GetAutoFillPreview( rSource, nEndX, nEndY ); + + return EMPTY_STRING; +} + +void ScDocument::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + USHORT nFormatNo, const ScMarkData& rMark ) +{ + PutInOrder( nStartCol, nEndCol ); + PutInOrder( nStartRow, nEndRow ); + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->AutoFormat( nStartCol, nStartRow, nEndCol, nEndRow, nFormatNo ); +} + +void ScDocument::GetAutoFormatData(SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + ScAutoFormatData& rData) +{ + if (VALIDTAB(nTab)) + { + if (pTab[nTab]) + { + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + pTab[nTab]->GetAutoFormatData(nStartCol, nStartRow, nEndCol, nEndRow, rData); + } + } +} + +// static +void ScDocument::GetSearchAndReplaceStart( const SvxSearchItem& rSearchItem, + SCCOL& rCol, SCROW& rRow ) +{ + USHORT nCommand = rSearchItem.GetCommand(); + BOOL bReplace = ( nCommand == SVX_SEARCHCMD_REPLACE || + nCommand == SVX_SEARCHCMD_REPLACE_ALL ); + if ( rSearchItem.GetBackward() ) + { + if ( rSearchItem.GetRowDirection() ) + { + if ( rSearchItem.GetPattern() ) + { + rCol = MAXCOL; + rRow = MAXROW+1; + } + else if ( bReplace ) + { + rCol = MAXCOL; + rRow = MAXROW; + } + else + { + rCol = MAXCOL+1; + rRow = MAXROW; + } + } + else + { + if ( rSearchItem.GetPattern() ) + { + rCol = MAXCOL+1; + rRow = MAXROW; + } + else if ( bReplace ) + { + rCol = MAXCOL; + rRow = MAXROW; + } + else + { + rCol = MAXCOL; + rRow = MAXROW+1; + } + } + } + else + { + if ( rSearchItem.GetRowDirection() ) + { + if ( rSearchItem.GetPattern() ) + { + rCol = 0; + rRow = (SCROW) -1; + } + else if ( bReplace ) + { + rCol = 0; + rRow = 0; + } + else + { + rCol = (SCCOL) -1; + rRow = 0; + } + } + else + { + if ( rSearchItem.GetPattern() ) + { + rCol = (SCCOL) -1; + rRow = 0; + } + else if ( bReplace ) + { + rCol = 0; + rRow = 0; + } + else + { + rCol = 0; + rRow = (SCROW) -1; + } + } + } +} + +BOOL ScDocument::SearchAndReplace(const SvxSearchItem& rSearchItem, + SCCOL& rCol, SCROW& rRow, SCTAB& rTab, + ScMarkData& rMark, + String& rUndoStr, ScDocument* pUndoDoc) +{ + //! getrennte Markierungen pro Tabelle verwalten !!!!!!!!!!!!! + + rMark.MarkToMulti(); + + BOOL bFound = FALSE; + if (VALIDTAB(rTab)) + { + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + USHORT nCommand = rSearchItem.GetCommand(); + if ( nCommand == SVX_SEARCHCMD_FIND_ALL || + nCommand == SVX_SEARCHCMD_REPLACE_ALL ) + { + for (nTab = 0; nTab <= MAXTAB; nTab++) + if (pTab[nTab]) + { + if (rMark.GetTableSelect(nTab)) + { + nCol = 0; + nRow = 0; + bFound |= pTab[nTab]->SearchAndReplace( + rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc ); + } + } + + // Markierung wird innen schon komplett gesetzt + } + else + { + nCol = rCol; + nRow = rRow; + if (rSearchItem.GetBackward()) + { + for (nTab = rTab; ((SCsTAB)nTab >= 0) && !bFound; nTab--) + if (pTab[nTab]) + { + if (rMark.GetTableSelect(nTab)) + { + bFound = pTab[nTab]->SearchAndReplace( + rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc ); + if (bFound) + { + rCol = nCol; + rRow = nRow; + rTab = nTab; + } + else + ScDocument::GetSearchAndReplaceStart( + rSearchItem, nCol, nRow ); + } + } + } + else + { + for (nTab = rTab; (nTab <= MAXTAB) && !bFound; nTab++) + if (pTab[nTab]) + { + if (rMark.GetTableSelect(nTab)) + { + bFound = pTab[nTab]->SearchAndReplace( + rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc ); + if (bFound) + { + rCol = nCol; + rRow = nRow; + rTab = nTab; + } + else + ScDocument::GetSearchAndReplaceStart( + rSearchItem, nCol, nRow ); + } + } + } + } + } + return bFound; +} + +BOOL ScDocument::IsFiltered( SCROW nRow, SCTAB nTab ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->IsFiltered( nRow ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + +// Outline anpassen + +BOOL ScDocument::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, SCTAB nTab, BOOL bShow ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->UpdateOutlineCol( nStartCol, nEndCol, bShow ); + + DBG_ERROR("missing tab"); + return FALSE; +} + +BOOL ScDocument::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, BOOL bShow ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->UpdateOutlineRow( nStartRow, nEndRow, bShow ); + + DBG_ERROR("missing tab"); + return FALSE; +} + +void ScDocument::Sort(SCTAB nTab, const ScSortParam& rSortParam, BOOL bKeepQuery) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + { + BOOL bOldDisableIdle = IsIdleDisabled(); + DisableIdle( TRUE ); + pTab[nTab]->Sort(rSortParam, bKeepQuery); + DisableIdle( bOldDisableIdle ); + } +} + +SCSIZE ScDocument::Query(SCTAB nTab, const ScQueryParam& rQueryParam, BOOL bKeepSub) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->Query((ScQueryParam&)rQueryParam, bKeepSub); + + DBG_ERROR("missing tab"); + return 0; +} + + +BOOL ScDocument::ValidQuery( SCROW nRow, SCTAB nTab, const ScQueryParam& rQueryParam, BOOL* pSpecial ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->ValidQuery( nRow, rQueryParam, pSpecial ); + + DBG_ERROR("missing tab"); + return FALSE; +} + + +void ScDocument::GetUpperCellString(SCCOL nCol, SCROW nRow, SCTAB nTab, String& rStr) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->GetUpperCellString( nCol, nRow, rStr ); + else + rStr.Erase(); +} + +BOOL ScDocument::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, SCTAB nTab, ScQueryParam& rQueryParam) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->CreateQueryParam(nCol1, nRow1, nCol2, nRow2, rQueryParam); + + DBG_ERROR("missing tab"); + return FALSE; +} + +BOOL ScDocument::HasAutoFilter( SCCOL nCurCol, SCROW nCurRow, SCTAB nCurTab ) +{ + ScDBData* pDBData = GetDBAtCursor( nCurCol, nCurRow, nCurTab ); + BOOL bHasAutoFilter = ( pDBData != NULL ); + + if ( pDBData ) + { + if ( pDBData->HasHeader() ) + { + SCCOL nCol; + SCROW nRow; + INT16 nFlag; + + ScQueryParam aParam; + pDBData->GetQueryParam( aParam ); + nRow = aParam.nRow1; + + for ( nCol=aParam.nCol1; nCol<=aParam.nCol2 && bHasAutoFilter; nCol++ ) + { + nFlag = ((ScMergeFlagAttr*) + GetAttr( nCol, nRow, nCurTab, ATTR_MERGE_FLAG ))-> + GetValue(); + + if ( (nFlag & SC_MF_AUTO) == 0 ) + bHasAutoFilter = FALSE; + } + } + else + bHasAutoFilter = FALSE; + } + + return bHasAutoFilter; +} + +BOOL ScDocument::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + SCTAB nTab ) +{ + return VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->HasColHeader( nStartCol, nStartRow, nEndCol, nEndRow ); + //if (VALIDTAB(nTab)) + // if (pTab[nTab]) + // return pTab[nTab]->HasColHeader( nStartCol, nStartRow, nEndCol, nEndRow ); + + //return FALSE; +} + +BOOL ScDocument::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + SCTAB nTab ) +{ + return VALIDTAB(nTab) && pTab[nTab] && pTab[nTab]->HasRowHeader( nStartCol, nStartRow, nEndCol, nEndRow ); + //if (VALIDTAB(nTab)) + // if (pTab[nTab]) + // return pTab[nTab]->HasRowHeader( nStartCol, nStartRow, nEndCol, nEndRow ); + + //return FALSE; +} + +// +// GetFilterEntries - Eintraege fuer AutoFilter-Listbox +// + +BOOL ScDocument::GetFilterEntries( SCCOL nCol, SCROW nRow, SCTAB nTab, TypedScStrCollection& rStrings, bool bFilter ) +{ + if ( ValidTab(nTab) && pTab[nTab] && pDBCollection ) + { + ScDBData* pDBData = pDBCollection->GetDBAtCursor(nCol, nRow, nTab, FALSE); //!?? + if (pDBData) + { + SCTAB nAreaTab; + SCCOL nStartCol; + SCROW nStartRow; + SCCOL nEndCol; + SCROW nEndRow; + pDBData->GetArea( nAreaTab, nStartCol, nStartRow, nEndCol, nEndRow ); + if (pDBData->HasHeader()) + ++nStartRow; + + ScQueryParam aParam; + pDBData->GetQueryParam( aParam ); + rStrings.SetCaseSensitive( aParam.bCaseSens ); + + // return all filter entries, if a filter condition is connected with a boolean OR + if ( bFilter ) + { + SCSIZE nEntryCount = aParam.GetEntryCount(); + for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i ) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if ( rEntry.eConnect != SC_AND ) + { + bFilter = false; + break; + } + } + } + + if ( bFilter ) + { + pTab[nTab]->GetFilteredFilterEntries( nCol, nStartRow, nEndRow, aParam, rStrings ); + } + else + { + pTab[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rStrings ); + } + + return TRUE; + } + } + + return FALSE; +} + +// +// GetFilterEntriesArea - Eintraege fuer Filter-Dialog +// + +BOOL ScDocument::GetFilterEntriesArea( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, + SCTAB nTab, TypedScStrCollection& rStrings ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + { + pTab[nTab]->GetFilterEntries( nCol, nStartRow, nEndRow, rStrings ); + return TRUE; + } + + return FALSE; +} + +// +// GetDataEntries - Eintraege fuer Auswahlliste-Listbox (keine Zahlen / Formeln) +// + +BOOL ScDocument::GetDataEntries( SCCOL nCol, SCROW nRow, SCTAB nTab, + TypedScStrCollection& rStrings, BOOL bLimit ) +{ + if( !bLimit ) + { + /* Try to generate the list from list validation. This part is skipped, + if bLimit==TRUE, because in that case this function is called to get + cell values for auto completion on input. */ + sal_uInt32 nValidation = static_cast< const SfxUInt32Item* >( GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA ) )->GetValue(); + if( nValidation ) + { + const ScValidationData* pData = GetValidationEntry( nValidation ); + if( pData && pData->FillSelectionList( rStrings, ScAddress( nCol, nRow, nTab ) ) ) + return TRUE; + } + } + + return ValidTab(nTab) && pTab[nTab] && pTab[nTab]->GetDataEntries( nCol, nRow, rStrings, bLimit ); + //if (ValidTab(nTab) && pTab[nTab]) + // return pTab[nTab]->GetDataEntries( nCol, nRow, rStrings, bLimit ); + + //return FALSE; +} + +// +// GetFormulaEntries - Eintraege fuer Formel-AutoEingabe +// + +// Funktionen werden als 1 schon vom InputHandler eingefuegt +#define SC_STRTYPE_NAMES 2 +#define SC_STRTYPE_DBNAMES 3 +#define SC_STRTYPE_HEADERS 4 + +BOOL ScDocument::GetFormulaEntries( TypedScStrCollection& rStrings ) +{ + USHORT i; + + // + // Bereichsnamen + // + + if ( pRangeName ) + { + USHORT nRangeCount = pRangeName->GetCount(); + for ( i=0; i<nRangeCount; i++ ) + { + ScRangeData* pData = (*pRangeName)[i]; + if (pData) + { + TypedStrData* pNew = new TypedStrData( pData->GetName(), 0.0, SC_STRTYPE_NAMES ); + if ( !rStrings.Insert(pNew) ) + delete pNew; + } + } + } + + // + // Datenbank-Bereiche + // + + if ( pDBCollection ) + { + USHORT nDBCount = pDBCollection->GetCount(); + for ( i=0; i<nDBCount; i++ ) + { + ScDBData* pData = (*pDBCollection)[i]; + if (pData) + { + TypedStrData* pNew = new TypedStrData( pData->GetName(), 0.0, SC_STRTYPE_DBNAMES ); + if ( !rStrings.Insert(pNew) ) + delete pNew; + } + } + } + + // + // Inhalte von Beschriftungsbereichen + // + + ScRangePairList* pLists[2]; + pLists[0] = GetColNameRanges(); + pLists[1] = GetRowNameRanges(); + for (USHORT nListNo=0; nListNo<2; nListNo++) + { + ScRangePairList* pList = pLists[nListNo]; + if (pList) + for ( ScRangePair* pPair = pList->First(); pPair; pPair = pList->Next() ) + { + ScRange aRange = pPair->GetRange(0); + ScCellIterator aIter( this, aRange ); + for ( ScBaseCell* pCell = aIter.GetFirst(); pCell; pCell = aIter.GetNext() ) + if ( pCell->HasStringData() ) + { + String aStr = pCell->GetStringData(); + TypedStrData* pNew = new TypedStrData( aStr, 0.0, SC_STRTYPE_HEADERS ); + if ( !rStrings.Insert(pNew) ) + delete pNew; + } + } + } + + return TRUE; +} + + +BOOL ScDocument::IsEmbedded() const +{ + return bIsEmbedded; +} + +void ScDocument::GetEmbedded( ScRange& rRange ) const +{ + rRange = aEmbedRange; +} + +Rectangle ScDocument::GetEmbeddedRect() const // 1/100 mm +{ + Rectangle aRect; + ScTable* pTable = pTab[aEmbedRange.aStart.Tab()]; + if (!pTable) + { + DBG_ERROR("GetEmbeddedRect ohne Tabelle"); + } + else + { + SCCOL i; + + for (i=0; i<aEmbedRange.aStart.Col(); i++) + aRect.Left() += pTable->GetColWidth(i); + aRect.Top() += pTable->GetRowHeight( 0, aEmbedRange.aStart.Row() - 1); + aRect.Right() = aRect.Left(); + for (i=aEmbedRange.aStart.Col(); i<=aEmbedRange.aEnd.Col(); i++) + aRect.Right() += pTable->GetColWidth(i); + aRect.Bottom() = aRect.Top(); + aRect.Bottom() += pTable->GetRowHeight( aEmbedRange.aStart.Row(), aEmbedRange.aEnd.Row()); + + aRect.Left() = (long) ( aRect.Left() * HMM_PER_TWIPS ); + aRect.Right() = (long) ( aRect.Right() * HMM_PER_TWIPS ); + aRect.Top() = (long) ( aRect.Top() * HMM_PER_TWIPS ); + aRect.Bottom() = (long) ( aRect.Bottom() * HMM_PER_TWIPS ); + } + return aRect; +} + +void ScDocument::SetEmbedded( const ScRange& rRange ) +{ + bIsEmbedded = TRUE; + aEmbedRange = rRange; +} + +void ScDocument::ResetEmbedded() +{ + bIsEmbedded = FALSE; + aEmbedRange = ScRange(); +} + +ScRange ScDocument::GetRange( SCTAB nTab, const Rectangle& rMMRect ) +{ + ScTable* pTable = pTab[nTab]; + if (!pTable) + { + DBG_ERROR("GetRange ohne Tabelle"); + return ScRange(); + } + + Rectangle aPosRect = rMMRect; + if ( IsNegativePage( nTab ) ) + ScDrawLayer::MirrorRectRTL( aPosRect ); // always with positive (LTR) values + + long nSize; + long nTwips; + long nAdd; + BOOL bEnd; + + nSize = 0; + nTwips = (long) (aPosRect.Left() / HMM_PER_TWIPS); + + SCCOL nX1 = 0; + bEnd = FALSE; + while (!bEnd) + { + nAdd = (long) pTable->GetColWidth(nX1); + if (nSize+nAdd <= nTwips+1 && nX1<MAXCOL) + { + nSize += nAdd; + ++nX1; + } + else + bEnd = TRUE; + } + + nTwips = (long) (aPosRect.Right() / HMM_PER_TWIPS); + + SCCOL nX2 = nX1; + bEnd = FALSE; + while (!bEnd) + { + nAdd = (long) pTable->GetColWidth(nX2); + if (nSize+nAdd < nTwips && nX2<MAXCOL) + { + nSize += nAdd; + ++nX2; + } + else + bEnd = TRUE; + } + + + nSize = 0; + nTwips = (long) (aPosRect.Top() / HMM_PER_TWIPS); + + SCROW nY1 = 0; + ScCoupledCompressedArrayIterator< SCROW, BYTE, USHORT> aIter( + *(pTable->GetRowFlagsArray()), nY1, MAXROW, CR_HIDDEN, 0, + *(pTable->GetRowHeightArray())); + bEnd = FALSE; + while (!bEnd && aIter) + { + nY1 = aIter.GetPos(); + nAdd = (long) *aIter; + if (nSize+nAdd <= nTwips+1 && nY1<MAXROW) + { + nSize += nAdd; + ++nY1; + ++aIter; + } + else + bEnd = TRUE; + } + if (!aIter) + nY1 = aIter.GetIterEnd(); // all hidden down to the bottom + + nTwips = (long) (aPosRect.Bottom() / HMM_PER_TWIPS); + + SCROW nY2 = nY1; + aIter.NewLimits( nY2, MAXROW); + bEnd = FALSE; + while (!bEnd && aIter) + { + nY2 = aIter.GetPos(); + nAdd = (long) *aIter; + if (nSize+nAdd < nTwips && nY2<MAXROW) + { + nSize += nAdd; + ++nY2; + ++aIter; + } + else + bEnd = TRUE; + } + if (!aIter) + nY2 = aIter.GetIterEnd(); // all hidden down to the bottom + + return ScRange( nX1,nY1,nTab, nX2,nY2,nTab ); +} + +void ScDocument::SetEmbedded( const Rectangle& rRect ) // aus VisArea (1/100 mm) +{ + bIsEmbedded = TRUE; + aEmbedRange = GetRange( nVisibleTab, rRect ); +} + +// VisArea auf Zellgrenzen anpassen + +void lcl_SnapHor( ScTable* pTable, long& rVal, SCCOL& rStartCol ) +{ + SCCOL nCol = 0; + long nTwips = (long) (rVal / HMM_PER_TWIPS); + long nSnap = 0; + while ( nCol<MAXCOL ) + { + long nAdd = pTable->GetColWidth(nCol); + if ( nSnap + nAdd/2 < nTwips || nCol < rStartCol ) + { + nSnap += nAdd; + ++nCol; + } + else + break; + } + rVal = (long) ( nSnap * HMM_PER_TWIPS ); + rStartCol = nCol; +} + +void lcl_SnapVer( ScTable* pTable, long& rVal, SCROW& rStartRow ) +{ + SCROW nRow = 0; + long nTwips = (long) (rVal / HMM_PER_TWIPS); + long nSnap = 0; + ScCoupledCompressedArrayIterator< SCROW, BYTE, USHORT> aIter( + *(pTable->GetRowFlagsArray()), nRow, MAXROW, CR_HIDDEN, 0, + *(pTable->GetRowHeightArray())); + while ( aIter ) + { + nRow = aIter.GetPos(); + long nAdd = *aIter; + if ( nSnap + nAdd/2 < nTwips || nRow < rStartRow ) + { + nSnap += nAdd; + ++nRow; + ++aIter; + } + else + break; + } + if (!aIter) + nRow = MAXROW; // all hidden down to the bottom + rVal = (long) ( nSnap * HMM_PER_TWIPS ); + rStartRow = nRow; +} + +void ScDocument::SnapVisArea( Rectangle& rRect ) const +{ + ScTable* pTable = pTab[nVisibleTab]; + if (!pTable) + { + DBG_ERROR("SetEmbedded ohne Tabelle"); + return; + } + + BOOL bNegativePage = IsNegativePage( nVisibleTab ); + if ( bNegativePage ) + ScDrawLayer::MirrorRectRTL( rRect ); // calculate with positive (LTR) values + + SCCOL nCol = 0; + lcl_SnapHor( pTable, rRect.Left(), nCol ); + ++nCol; // mindestens eine Spalte + lcl_SnapHor( pTable, rRect.Right(), nCol ); + + SCROW nRow = 0; + lcl_SnapVer( pTable, rRect.Top(), nRow ); + ++nRow; // mindestens eine Zeile + lcl_SnapVer( pTable, rRect.Bottom(), nRow ); + + if ( bNegativePage ) + ScDrawLayer::MirrorRectRTL( rRect ); // back to real rectangle +} + +ScDocProtection* ScDocument::GetDocProtection() const +{ + return pDocProtection.get(); +} + +void ScDocument::SetDocProtection(const ScDocProtection* pProtect) +{ + if (pProtect) + pDocProtection.reset(new ScDocProtection(*pProtect)); + else + pDocProtection.reset(NULL); +} + +BOOL ScDocument::IsDocProtected() const +{ + return pDocProtection.get() && pDocProtection->isProtected(); +} + +BOOL ScDocument::IsDocEditable() const +{ + // import into read-only document is possible + return !IsDocProtected() && ( bImportingXML || mbChangeReadOnlyEnabled || !pShell || !pShell->IsReadOnly() ); +} + +BOOL ScDocument::IsTabProtected( SCTAB nTab ) const +{ + if (VALIDTAB(nTab) && pTab[nTab]) + return pTab[nTab]->IsProtected(); + + DBG_ERROR("Falsche Tabellennummer"); + return FALSE; +} + +ScTableProtection* ScDocument::GetTabProtection( SCTAB nTab ) const +{ + if (VALIDTAB(nTab) && pTab[nTab]) + return pTab[nTab]->GetProtection(); + + return NULL; +} + +void ScDocument::SetTabProtection(SCTAB nTab, const ScTableProtection* pProtect) +{ + if (!ValidTab(nTab)) + return; + + pTab[nTab]->SetProtection(pProtect); +} + +void ScDocument::CopyTabProtection(SCTAB nTabSrc, SCTAB nTabDest) +{ + if (!ValidTab(nTabSrc) || !ValidTab(nTabDest)) + return; + + pTab[nTabDest]->SetProtection( pTab[nTabSrc]->GetProtection() ); +} + +const ScDocOptions& ScDocument::GetDocOptions() const +{ + DBG_ASSERT( pDocOptions, "No DocOptions! :-(" ); + return *pDocOptions; +} + +void ScDocument::SetDocOptions( const ScDocOptions& rOpt ) +{ + DBG_ASSERT( pDocOptions, "No DocOptions! :-(" ); + *pDocOptions = rOpt; + + xPoolHelper->SetFormTableOpt(rOpt); +} + +const ScViewOptions& ScDocument::GetViewOptions() const +{ + DBG_ASSERT( pViewOptions, "No ViewOptions! :-(" ); + return *pViewOptions; +} + +void ScDocument::SetViewOptions( const ScViewOptions& rOpt ) +{ + DBG_ASSERT( pViewOptions, "No ViewOptions! :-(" ); + *pViewOptions = rOpt; +} + +void ScDocument::GetLanguage( LanguageType& rLatin, LanguageType& rCjk, LanguageType& rCtl ) const +{ + rLatin = eLanguage; + rCjk = eCjkLanguage; + rCtl = eCtlLanguage; +} + +void ScDocument::SetLanguage( LanguageType eLatin, LanguageType eCjk, LanguageType eCtl ) +{ + eLanguage = eLatin; + eCjkLanguage = eCjk; + eCtlLanguage = eCtl; + if ( xPoolHelper.isValid() ) + { + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + pPool->SetPoolDefaultItem( SvxLanguageItem( eLanguage, ATTR_FONT_LANGUAGE ) ); + pPool->SetPoolDefaultItem( SvxLanguageItem( eCjkLanguage, ATTR_CJK_FONT_LANGUAGE ) ); + pPool->SetPoolDefaultItem( SvxLanguageItem( eCtlLanguage, ATTR_CTL_FONT_LANGUAGE ) ); + } + + UpdateDrawLanguages(); // set edit engine defaults in drawing layer pool +} + +void ScDocument::SetDrawDefaults() +{ + bSetDrawDefaults = TRUE; + UpdateDrawDefaults(); +} + +Rectangle ScDocument::GetMMRect( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) +{ + if (!ValidTab(nTab) || !pTab[nTab]) + { + DBG_ERROR("GetMMRect: falsche Tabelle"); + return Rectangle(0,0,0,0); + } + + SCCOL i; + Rectangle aRect; + + for (i=0; i<nStartCol; i++) + aRect.Left() += GetColWidth(i,nTab); + aRect.Top() += FastGetRowHeight( 0, nStartRow-1, nTab); + + aRect.Right() = aRect.Left(); + aRect.Bottom() = aRect.Top(); + + for (i=nStartCol; i<=nEndCol; i++) + aRect.Right() += GetColWidth(i,nTab); + aRect.Bottom() += FastGetRowHeight( nStartRow, nEndRow, nTab); + + aRect.Left() = (long)(aRect.Left() * HMM_PER_TWIPS); + aRect.Right() = (long)(aRect.Right() * HMM_PER_TWIPS); + aRect.Top() = (long)(aRect.Top() * HMM_PER_TWIPS); + aRect.Bottom() = (long)(aRect.Bottom() * HMM_PER_TWIPS); + + if ( IsNegativePage( nTab ) ) + ScDrawLayer::MirrorRectRTL( aRect ); + + return aRect; +} + +void ScDocument::SetExtDocOptions( ScExtDocOptions* pNewOptions ) +{ + delete pExtDocOptions; + pExtDocOptions = pNewOptions; +} + +void ScDocument::DoMergeContents( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow ) +{ + String aEmpty; + String aTotal; + String aCellStr; + SCCOL nCol; + SCROW nRow; + for (nRow=nStartRow; nRow<=nEndRow; nRow++) + for (nCol=nStartCol; nCol<=nEndCol; nCol++) + { + GetString(nCol,nRow,nTab,aCellStr); + if (aCellStr.Len()) + { + if (aTotal.Len()) + aTotal += ' '; + aTotal += aCellStr; + } + if (nCol != nStartCol || nRow != nStartRow) + SetString(nCol,nRow,nTab,aEmpty); + } + + SetString(nStartCol,nStartRow,nTab,aTotal); +} + +void ScDocument::DoMerge( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, bool bDeleteCaptions ) +{ + ScMergeAttr aAttr( nEndCol-nStartCol+1, nEndRow-nStartRow+1 ); + ApplyAttr( nStartCol, nStartRow, nTab, aAttr ); + + if ( nEndCol > nStartCol ) + ApplyFlagsTab( nStartCol+1, nStartRow, nEndCol, nStartRow, nTab, SC_MF_HOR ); + if ( nEndRow > nStartRow ) + ApplyFlagsTab( nStartCol, nStartRow+1, nStartCol, nEndRow, nTab, SC_MF_VER ); + if ( nEndCol > nStartCol && nEndRow > nStartRow ) + ApplyFlagsTab( nStartCol+1, nStartRow+1, nEndCol, nEndRow, nTab, SC_MF_HOR | SC_MF_VER ); + + // remove all covered notes (removed captions are collected by drawing undo if active) + USHORT nDelFlag = IDF_NOTE | (bDeleteCaptions ? 0 : IDF_NOCAPTIONS); + if( nStartCol < nEndCol ) + DeleteAreaTab( nStartCol + 1, nStartRow, nEndCol, nStartRow, nTab, nDelFlag ); + if( nStartRow < nEndRow ) + DeleteAreaTab( nStartCol, nStartRow + 1, nEndCol, nEndRow, nTab, nDelFlag ); +} + +void ScDocument::RemoveMerge( SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + const ScMergeAttr* pAttr = (const ScMergeAttr*) + GetAttr( nCol, nRow, nTab, ATTR_MERGE ); + + if ( pAttr->GetColMerge() <= 1 && pAttr->GetRowMerge() <= 1 ) + return; + + SCCOL nEndCol = nCol + pAttr->GetColMerge() - 1; + SCROW nEndRow = nRow + pAttr->GetRowMerge() - 1; + + RemoveFlagsTab( nCol, nRow, nEndCol, nEndRow, nTab, SC_MF_HOR | SC_MF_VER ); + + const ScMergeAttr* pDefAttr = (const ScMergeAttr*) + &xPoolHelper->GetDocPool()->GetDefaultItem( ATTR_MERGE ); + ApplyAttr( nCol, nRow, nTab, *pDefAttr ); +} + +void ScDocument::ExtendPrintArea( OutputDevice* pDev, SCTAB nTab, + SCCOL nStartCol, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ExtendPrintArea( pDev, nStartCol, nStartRow, rEndCol, nEndRow ); +} + +void ScDocument::IncSizeRecalcLevel( SCTAB nTab ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->IncRecalcLevel(); +} + +void ScDocument::DecSizeRecalcLevel( SCTAB nTab ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->DecRecalcLevel(); +} + + + + diff --git a/sc/source/core/data/documen4.cxx b/sc/source/core/data/documen4.cxx new file mode 100644 index 000000000000..7313ebbf83f5 --- /dev/null +++ b/sc/source/core/data/documen4.cxx @@ -0,0 +1,1197 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen4.cxx,v $ + * $Revision: 1.23.102.2 $ + * + * 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 <svtools/intitem.hxx> +#include <svtools/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: kein Value aber von Formel referiert + if (eFType == CELLTYPE_FORMULA && (eVType == CELLTYPE_VALUE + || eVType == CELLTYPE_NOTE) ) + { + 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.AddString( sValStr.GetBuffer() ); + 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( USHORT& 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; +} + + + + + diff --git a/sc/source/core/data/documen5.cxx b/sc/source/core/data/documen5.cxx new file mode 100644 index 000000000000..0be1e6717891 --- /dev/null +++ b/sc/source/core/data/documen5.cxx @@ -0,0 +1,1016 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen5.cxx,v $ + * $Revision: 1.34 $ + * + * 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 <com/sun/star/util/XModifiable.hpp> +#include <com/sun/star/chart/ChartDataRowSource.hpp> +#include <com/sun/star/chart2/XChartDocument.hpp> +#include <com/sun/star/chart2/data/XDataProvider.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/XEmbeddedObject.hpp> + + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +// INCLUDE --------------------------------------------------------------- + +#include <sfx2/objsh.hxx> +#include <svx/svditer.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdpage.hxx> + +//REMOVE #ifndef SO2_DECL_SVINPLACEOBJECT_DEFINED +//REMOVE #define SO2_DECL_SVINPLACEOBJECT_DEFINED +//REMOVE SO2_DECL_REF(SvInPlaceObject) +//REMOVE #endif + +#include "document.hxx" +#include "drwlayer.hxx" +#include "chartarr.hxx" +#include "chartlis.hxx" +#include "chartlock.hxx" +#include "refupdat.hxx" +#include <tools/globname.hxx> +#include <sot/exchange.hxx> + +#include "miscuno.hxx" +#include "chart2uno.hxx" + +using namespace ::com::sun::star; + +// ----------------------------------------------------------------------- + +void lcl_GetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc, + uno::Sequence< rtl::OUString >& rRanges ) +{ + rRanges.realloc(0); + uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY ); + if( !xDataSource.is() ) + return; + //uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider(); + + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() ); + rRanges.realloc(2*aLabeledDataSequences.getLength()); + sal_Int32 nRealCount=0; + for( sal_Int32 nN=0;nN<aLabeledDataSequences.getLength();nN++) + { + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSequence( aLabeledDataSequences[nN] ); + if(!xLabeledSequence.is()) + continue; + uno::Reference< chart2::data::XDataSequence > xLabel( xLabeledSequence->getLabel()); + uno::Reference< chart2::data::XDataSequence > xValues( xLabeledSequence->getValues()); + + if( xLabel.is()) + rRanges[nRealCount++] = xLabel->getSourceRangeRepresentation(); + if( xValues.is()) + rRanges[nRealCount++] = xValues->getSourceRangeRepresentation(); + } + rRanges.realloc(nRealCount); +} + +void lcl_SetChartRanges( const uno::Reference< chart2::XChartDocument >& xChartDoc, + const uno::Sequence< rtl::OUString >& rRanges ) +{ + uno::Reference< chart2::data::XDataSource > xDataSource( xChartDoc, uno::UNO_QUERY ); + if( !xDataSource.is() ) + return; + uno::Reference< chart2::data::XDataProvider > xDataProvider = xChartDoc->getDataProvider(); + if( !xDataProvider.is() ) + return; + + uno::Reference< frame::XModel > xModel( xChartDoc, uno::UNO_QUERY ); + if( xModel.is() ) + xModel->lockControllers(); + + try + { + rtl::OUString aPropertyNameRole( ::rtl::OUString::createFromAscii("Role") ); + + uno::Sequence< uno::Reference< chart2::data::XLabeledDataSequence > > aLabeledDataSequences( xDataSource->getDataSequences() ); + sal_Int32 nRange=0; + for( sal_Int32 nN=0; (nN<aLabeledDataSequences.getLength()) && (nRange<rRanges.getLength()); nN++ ) + { + uno::Reference< chart2::data::XLabeledDataSequence > xLabeledSequence( aLabeledDataSequences[nN] ); + if(!xLabeledSequence.is()) + continue; + uno::Reference< beans::XPropertySet > xLabel( xLabeledSequence->getLabel(), uno::UNO_QUERY ); + uno::Reference< beans::XPropertySet > xValues( xLabeledSequence->getValues(), uno::UNO_QUERY ); + + if( xLabel.is()) + { + // the range string must be in Calc A1 format. + uno::Reference< chart2::data::XDataSequence > xNewSeq( + xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] )); + + uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY ); + if( xNewProps.is() ) + xNewProps->setPropertyValue( aPropertyNameRole, xLabel->getPropertyValue( aPropertyNameRole ) ); + + xLabeledSequence->setLabel( xNewSeq ); + } + + if( !(nRange<rRanges.getLength()) ) + break; + + if( xValues.is()) + { + // the range string must be in Calc A1 format. + uno::Reference< chart2::data::XDataSequence > xNewSeq( + xDataProvider->createDataSequenceByRangeRepresentation( rRanges[nRange++] )); + + uno::Reference< beans::XPropertySet > xNewProps( xNewSeq, uno::UNO_QUERY ); + if( xNewProps.is() ) + xNewProps->setPropertyValue( aPropertyNameRole, xValues->getPropertyValue( aPropertyNameRole ) ); + + xLabeledSequence->setValues( xNewSeq ); + } + } + } + catch ( uno::Exception& ex ) + { + (void)ex; + DBG_ERROR("Exception in lcl_SetChartRanges - invalid range string?"); + } + + if( xModel.is() ) + xModel->unlockControllers(); +} + +void lcl_GetChartParameters( const uno::Reference< chart2::XChartDocument >& xChartDoc, + rtl::OUString& rRanges, chart::ChartDataRowSource& rDataRowSource, + bool& rHasCategories, bool& rFirstCellAsLabel ) +{ + rHasCategories = rFirstCellAsLabel = false; // default if not in sequence + + uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY ); + + uno::Reference< chart2::data::XDataSource > xDataSource = xReceiver->getUsedData(); + uno::Reference< chart2::data::XDataProvider > xProvider = xChartDoc->getDataProvider(); + + if ( xProvider.is() ) + { + uno::Sequence< beans::PropertyValue > aArgs( xProvider->detectArguments( xDataSource ) ); + + const beans::PropertyValue* pPropArray = aArgs.getConstArray(); + long nPropCount = aArgs.getLength(); + for (long i = 0; i < nPropCount; i++) + { + const beans::PropertyValue& rProp = pPropArray[i]; + String aPropName(rProp.Name); + + if (aPropName.EqualsAscii( "CellRangeRepresentation" )) + rProp.Value >>= rRanges; + else if (aPropName.EqualsAscii( "DataRowSource" )) + rDataRowSource = (chart::ChartDataRowSource)ScUnoHelpFunctions::GetEnumFromAny( rProp.Value ); + else if (aPropName.EqualsAscii( "HasCategories" )) + rHasCategories = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + else if (aPropName.EqualsAscii( "FirstCellAsLabel" )) + rFirstCellAsLabel = ScUnoHelpFunctions::GetBoolFromAny( rProp.Value ); + } + } +} + +void lcl_SetChartParameters( const uno::Reference< chart2::data::XDataReceiver >& xReceiver, + const rtl::OUString& rRanges, chart::ChartDataRowSource eDataRowSource, + bool bHasCategories, bool bFirstCellAsLabel ) +{ + if ( xReceiver.is() ) + { + uno::Sequence< beans::PropertyValue > aArgs( 4 ); + aArgs[0] = beans::PropertyValue( + ::rtl::OUString::createFromAscii("CellRangeRepresentation"), -1, + uno::makeAny( rRanges ), beans::PropertyState_DIRECT_VALUE ); + aArgs[1] = beans::PropertyValue( + ::rtl::OUString::createFromAscii("HasCategories"), -1, + uno::makeAny( bHasCategories ), beans::PropertyState_DIRECT_VALUE ); + aArgs[2] = beans::PropertyValue( + ::rtl::OUString::createFromAscii("FirstCellAsLabel"), -1, + uno::makeAny( bFirstCellAsLabel ), beans::PropertyState_DIRECT_VALUE ); + aArgs[3] = beans::PropertyValue( + ::rtl::OUString::createFromAscii("DataRowSource"), -1, + uno::makeAny( eDataRowSource ), beans::PropertyState_DIRECT_VALUE ); + xReceiver->setArguments( aArgs ); + } +} + +// update charts after loading old document + +void ScDocument::UpdateAllCharts() +{ + if ( !pDrawLayer || !pShell ) + return; + + USHORT nDataCount = pChartCollection->GetCount(); + if ( !nDataCount ) + return ; // nothing to do + + USHORT nPos; + + for (SCTAB nTab=0; nTab<=MAXTAB; nTab++) + { + if (pTab[nTab]) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + ScRange aRange; + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + String aIPName = ((SdrOle2Obj*)pObject)->GetPersistName(); + + for (nPos=0; nPos<nDataCount; nPos++) + { + ScChartArray* pChartObj = (*pChartCollection)[nPos]; + if (pChartObj->GetName() == aIPName) + { + ScRangeListRef aRanges = pChartObj->GetRangeList(); + String sRangeStr; + aRanges->Format( sRangeStr, SCR_ABS_3D, this, GetAddressConvention() ); + + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + bool bHasCategories = pChartObj->HasRowHeaders(); + bool bFirstCellAsLabel = pChartObj->HasColHeaders(); + + // Calc -> DataProvider + uno::Reference< chart2::data::XDataProvider > xDataProvider = + new ScChart2DataProvider( this ); + // Chart -> DataReceiver + uno::Reference< chart2::data::XDataReceiver > xReceiver; + uno::Reference< embed::XComponentSupplier > xCompSupp( xIPObj, uno::UNO_QUERY ); + if( xCompSupp.is()) + xReceiver.set( xCompSupp->getComponent(), uno::UNO_QUERY ); + if( xReceiver.is()) + { + // connect + xReceiver->attachDataProvider( xDataProvider ); + uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( + pShell->GetModel(), uno::UNO_QUERY ); + xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier ); + + lcl_SetChartParameters( xReceiver, sRangeStr, eDataRowSource, + bHasCategories, bFirstCellAsLabel ); + } + + ScChartListener* pCL = new ScChartListener( + aIPName, this, pChartObj->GetRangeList() ); + pChartListenerCollection->Insert( pCL ); + pCL->StartListeningTo(); + } + } + } + } + pObject = aIter.Next(); + } + } + } + + pChartCollection->FreeAll(); +} + +BOOL ScDocument::HasChartAtPoint( SCTAB nTab, const Point& rPos, String* pName ) +{ + if (pDrawLayer && pTab[nTab]) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + pObject->GetCurrentBoundRect().IsInside(rPos) ) + { + // auch Chart-Objekte die nicht in der Collection sind + + if (IsChart(pObject)) + { + if (pName) + *pName = ((SdrOle2Obj*)pObject)->GetPersistName(); + return TRUE; + } + } + pObject = aIter.Next(); + } + } + + if (pName) + pName->Erase(); + return FALSE; // nix gefunden +} + +void ScDocument::UpdateChartArea( const String& rChartName, + const ScRange& rNewArea, BOOL bColHeaders, BOOL bRowHeaders, + BOOL bAdd ) +{ + ScRangeListRef aRLR( new ScRangeList ); + aRLR->Append( rNewArea ); + UpdateChartArea( rChartName, aRLR, bColHeaders, bRowHeaders, bAdd ); +} + +uno::Reference< chart2::XChartDocument > ScDocument::GetChartByName( const String& rChartName ) +{ + uno::Reference< chart2::XChartDocument > xReturn; + + if (pDrawLayer) + { + sal_uInt16 nCount = pDrawLayer->GetPageCount(); + for (sal_uInt16 nTab=0; nTab<nCount; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(nTab); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + ((SdrOle2Obj*)pObject)->GetPersistName() == rChartName ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent(); + xReturn.set( uno::Reference< chart2::XChartDocument >( xComponent, uno::UNO_QUERY ) ); + } + return xReturn; + } + pObject = aIter.Next(); + } + } + } + return xReturn; +} +void ScDocument::GetChartRanges( const String& rChartName, ::std::vector< ScRangeList >& rRangesVector, ScDocument* pSheetNameDoc ) +{ + rRangesVector.clear(); + uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( rChartName ) ); + if ( xChartDoc.is() ) + { + uno::Sequence< rtl::OUString > aRangeStrings; + lcl_GetChartRanges( xChartDoc, aRangeStrings ); + for( sal_Int32 nN=0; nN<aRangeStrings.getLength(); nN++ ) + { + ScRangeList aRanges; + aRanges.Parse( aRangeStrings[nN], pSheetNameDoc, SCA_VALID, pSheetNameDoc->GetAddressConvention() ); + rRangesVector.push_back(aRanges); + } + } +} + +void ScDocument::SetChartRanges( const String& rChartName, const ::std::vector< ScRangeList >& rRangesVector ) +{ + uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( rChartName ) ); + if ( xChartDoc.is() ) + { + sal_Int32 nCount = static_cast<sal_Int32>( rRangesVector.size() ); + uno::Sequence< rtl::OUString > aRangeStrings(nCount); + for( sal_Int32 nN=0; nN<nCount; nN++ ) + { + ScRangeList aScRangeList( rRangesVector[nN] ); + String sRangeStr; // This range must be in Calc A1 format. + aScRangeList.Format( sRangeStr, SCR_ABS_3D, this ); + aRangeStrings[nN]=sRangeStr; + } + lcl_SetChartRanges( xChartDoc, aRangeStrings ); + } +} + +void ScDocument::GetOldChartParameters( const String& rName, + ScRangeList& rRanges, BOOL& rColHeaders, BOOL& rRowHeaders ) +{ + // used for undo of changing chart source area + + if (!pDrawLayer) + return; + + sal_uInt16 nCount = pDrawLayer->GetPageCount(); + for (sal_uInt16 nTab=0; nTab<nCount; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(nTab); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + ((SdrOle2Obj*)pObject)->GetPersistName() == rName ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent(); + uno::Reference< chart2::XChartDocument > xChartDoc( xComponent, uno::UNO_QUERY ); + if ( xChartDoc.is() ) + { + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + bool bHasCategories = false; + bool bFirstCellAsLabel = false; + rtl::OUString aRangesStr; + lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel ); + + rRanges.Parse( aRangesStr, this ); + if ( eDataRowSource == chart::ChartDataRowSource_COLUMNS ) + { + rRowHeaders = bHasCategories; + rColHeaders = bFirstCellAsLabel; + } + else + { + rColHeaders = bHasCategories; + rRowHeaders = bFirstCellAsLabel; + } + } + } + return; + } + pObject = aIter.Next(); + } + } +} + +void ScDocument::UpdateChartArea( const String& rChartName, + const ScRangeListRef& rNewList, BOOL bColHeaders, BOOL bRowHeaders, + BOOL bAdd ) +{ + if (!pDrawLayer) + return; + + for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + ((SdrOle2Obj*)pObject)->GetPersistName() == rChartName ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent(); + uno::Reference< chart2::XChartDocument > xChartDoc( xComponent, uno::UNO_QUERY ); + uno::Reference< chart2::data::XDataReceiver > xReceiver( xComponent, uno::UNO_QUERY ); + if ( xChartDoc.is() && xReceiver.is() ) + { + ScRangeListRef aNewRanges; + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + bool bHasCategories = false; + bool bFirstCellAsLabel = false; + rtl::OUString aRangesStr; + lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel ); + + sal_Bool bInternalData = xChartDoc->hasInternalDataProvider(); + + if ( bAdd && !bInternalData ) + { + // append to old ranges, keep other settings + + aNewRanges = new ScRangeList; + aNewRanges->Parse( aRangesStr, this ); + + ULONG nAddCount = rNewList->Count(); + for ( ULONG nAdd=0; nAdd<nAddCount; nAdd++ ) + aNewRanges->Append( *rNewList->GetObject(nAdd) ); + } + else + { + // directly use new ranges (only eDataRowSource is used from old settings) + + if ( eDataRowSource == chart::ChartDataRowSource_COLUMNS ) + { + bHasCategories = bRowHeaders; + bFirstCellAsLabel = bColHeaders; + } + else + { + bHasCategories = bColHeaders; + bFirstCellAsLabel = bRowHeaders; + } + aNewRanges = rNewList; + } + + if ( bInternalData && pShell ) + { + // Calc -> DataProvider + uno::Reference< chart2::data::XDataProvider > xDataProvider = new ScChart2DataProvider( this ); + xReceiver->attachDataProvider( xDataProvider ); + uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( + pShell->GetModel(), uno::UNO_QUERY ); + xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier ); + } + + String sRangeStr; + aNewRanges->Format( sRangeStr, SCR_ABS_3D, this, GetAddressConvention() ); + + lcl_SetChartParameters( xReceiver, sRangeStr, eDataRowSource, bHasCategories, bFirstCellAsLabel ); + + pChartListenerCollection->ChangeListening( rChartName, aNewRanges ); + + // ((SdrOle2Obj*)pObject)->GetNewReplacement(); + // pObject->ActionChanged(); + + return; // nicht weitersuchen + } + } + } + pObject = aIter.Next(); + } + } +} + +void ScDocument::UpdateChart( const String& rChartName ) +{ + if (!pDrawLayer || bInDtorClear) + return; + + for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + ((SdrOle2Obj*)pObject)->GetPersistName() == rChartName ) + { + //@todo?: maybe we need a notification + //from the calc to the chart in future + //that calc content has changed + // ((SdrOle2Obj*)pObject)->GetNewReplacement(); + + // Load the object and set modified + + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + try + { + uno::Reference< util::XModifiable > xModif( xIPObj->getComponent(), uno::UNO_QUERY_THROW ); + if( apTemporaryChartLock.get() ) + apTemporaryChartLock->AlsoLockThisChart( uno::Reference< frame::XModel >( xModif, uno::UNO_QUERY ) ); + xModif->setModified( sal_True ); + } + catch ( uno::Exception& ) + { + } + } + + // repaint + + pObject->ActionChanged(); + + // After the update, chart keeps track of its own data source ranges, + // the listener doesn't need to listen anymore. + + pChartListenerCollection->ChangeListening( rChartName, new ScRangeList ); + + return; + + /* old chart: + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + const SchMemChart* pChartData = SchDLL::GetChartData(xIPObj); + if ( pChartData ) + { + ScChartArray aArray( this, *pChartData ); + + SchMemChart* pMemChart = aArray.CreateMemChart(); + ScChartArray::CopySettings( *pMemChart, *pChartData ); + + // #57655# Chart-Update ohne geaenderte Einstellungen (MemChart) + // soll das Dokument nicht auf modified setzen (z.B. in frisch + // geladenem Dokument durch initiales Recalc) + + // #72576# disable SetModified for readonly documents only + + sal_Bool bEnabled = ( (pShell && pShell->IsReadOnly()) || IsImportingXML() ); + sal_Bool bModified = sal_False; + uno::Reference< util::XModifiable > xModif; + + if ( bEnabled ) + { + try + { + xModif = + uno::Reference< util::XModifiable >( xIPObj->getComponent(), uno::UNO_QUERY_THROW ); + bModified = xModif->isModified(); + } + catch( uno::Exception& ) + { + bEnabled = sal_False; + } + } + + SchDLL::Update( xIPObj, pMemChart, pWindow ); + ((SdrOle2Obj*)pObject)->GetNewReplacement(); + delete pMemChart; + + // Dies veranlaesst Chart zum sofortigen Update + //SvData aEmpty; + //aIPObj->SendDataChanged( aEmpty ); + + // the method below did nothing in SO7 +//REMOVE aIPObj->SendViewChanged(); + + // redraw only + pObject->ActionChanged(); + // pObject->SendRepaintBroadcast(); + + if ( bEnabled && xModif.is() ) + { + try + { + if ( xModif->isModified() != bModified ) + xModif->setModified( bModified ); + } + catch ( uno::Exception& ) + {} + } + + return; // nicht weitersuchen + } + } + */ + } + pObject = aIter.Next(); + } + } +} + +void ScDocument::RestoreChartListener( const String& rName ) +{ + // Read the data ranges from the chart object, and start listening to those ranges again + // (called when a chart is saved, because then it might be swapped out and stop listening itself). + + uno::Reference< embed::XEmbeddedObject > xObject = FindOleObjectByName( rName ); + if ( xObject.is() ) + { + uno::Reference< util::XCloseable > xComponent = xObject->getComponent(); + uno::Reference< chart2::XChartDocument > xChartDoc( xComponent, uno::UNO_QUERY ); + uno::Reference< chart2::data::XDataReceiver > xReceiver( xComponent, uno::UNO_QUERY ); + if ( xChartDoc.is() && xReceiver.is() && !xChartDoc->hasInternalDataProvider()) + { + uno::Sequence<rtl::OUString> aRepresentations( xReceiver->getUsedRangeRepresentations() ); + ScRangeListRef aRanges = new ScRangeList; + sal_Int32 nRangeCount = aRepresentations.getLength(); + for ( sal_Int32 i=0; i<nRangeCount; i++ ) + { + ScRange aRange; + ScAddress::Details aDetails(GetAddressConvention(), 0, 0); + if ( aRange.ParseAny( aRepresentations[i], this, aDetails ) & SCA_VALID ) + aRanges->Append( aRange ); + } + + pChartListenerCollection->ChangeListening( rName, aRanges ); + } + } +} + +void ScDocument::UpdateChartRef( UpdateRefMode eUpdateRefMode, + SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if (!pDrawLayer) + return; + + USHORT nChartCount = pChartListenerCollection->GetCount(); + for ( USHORT nIndex = 0; nIndex < nChartCount; nIndex++ ) + { + ScChartListener* pChartListener = + (ScChartListener*) (pChartListenerCollection->At(nIndex)); + ScRangeListRef aRLR( pChartListener->GetRangeList() ); + ScRangeListRef aNewRLR( new ScRangeList ); + BOOL bChanged = FALSE; + BOOL bDataChanged = FALSE; + for ( ScRangePtr pR = aRLR->First(); pR; pR = aRLR->Next() ) + { + SCCOL theCol1 = pR->aStart.Col(); + SCROW theRow1 = pR->aStart.Row(); + SCTAB theTab1 = pR->aStart.Tab(); + SCCOL theCol2 = pR->aEnd.Col(); + SCROW theRow2 = pR->aEnd.Row(); + SCTAB theTab2 = pR->aEnd.Tab(); + ScRefUpdateRes eRes = ScRefUpdate::Update( + this, eUpdateRefMode, + nCol1,nRow1,nTab1, nCol2,nRow2,nTab2, + nDx,nDy,nDz, + theCol1,theRow1,theTab1, + theCol2,theRow2,theTab2 ); + if ( eRes != UR_NOTHING ) + { + bChanged = TRUE; + aNewRLR->Append( ScRange( + theCol1, theRow1, theTab1, + theCol2, theRow2, theTab2 )); + if ( eUpdateRefMode == URM_INSDEL + && !bDataChanged + && (eRes == UR_INVALID || + ((pR->aEnd.Col() - pR->aStart.Col() + != theCol2 - theCol1) + || (pR->aEnd.Row() - pR->aStart.Row() + != theRow2 - theRow1) + || (pR->aEnd.Tab() - pR->aStart.Tab() + != theTab2 - theTab1))) ) + { + bDataChanged = TRUE; + } + } + else + aNewRLR->Append( *pR ); + } + if ( bChanged ) + { +#if 0 + if ( nDz != 0 ) + { // #81844# sheet to be deleted or inserted or moved + // => no valid sheet names for references right now + pChartListener->ChangeListening( aNewRLR, bDataChanged ); + pChartListener->ScheduleSeriesRanges(); + } + else +#endif + { +// SetChartRangeList( pChartListener->GetString(), aNewRLR ); +// pChartListener->ChangeListening( aNewRLR, bDataChanged ); + + // Force the chart to be loaded now, so it registers itself for UNO events. + // UNO broadcasts are done after UpdateChartRef, so the chart will get this + // reference change. + + uno::Reference< embed::XEmbeddedObject > xIPObj = FindOleObjectByName( pChartListener->GetString() ); + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + // After the change, chart keeps track of its own data source ranges, + // the listener doesn't need to listen anymore. + + pChartListener->ChangeListening( new ScRangeList, bDataChanged ); + } + } + } +} + + +void ScDocument::SetChartRangeList( const String& rChartName, + const ScRangeListRef& rNewRangeListRef ) +{ + // called from ChartListener + + if (!pDrawLayer) + return; + + for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + ((SdrOle2Obj*)pObject)->GetPersistName() == rChartName ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + if ( xIPObj.is() ) + { + svt::EmbeddedObjectRef::TryRunningState( xIPObj ); + + uno::Reference< util::XCloseable > xComponent = xIPObj->getComponent(); + uno::Reference< chart2::XChartDocument > xChartDoc( xComponent, uno::UNO_QUERY ); + uno::Reference< chart2::data::XDataReceiver > xReceiver( xComponent, uno::UNO_QUERY ); + if ( xChartDoc.is() && xReceiver.is() ) + { + ScRangeListRef aNewRanges; + chart::ChartDataRowSource eDataRowSource = chart::ChartDataRowSource_COLUMNS; + bool bHasCategories = false; + bool bFirstCellAsLabel = false; + rtl::OUString aRangesStr; + lcl_GetChartParameters( xChartDoc, aRangesStr, eDataRowSource, bHasCategories, bFirstCellAsLabel ); + + String sRangeStr; + rNewRangeListRef->Format( sRangeStr, SCR_ABS_3D, this, GetAddressConvention() ); + + lcl_SetChartParameters( xReceiver, sRangeStr, eDataRowSource, bHasCategories, bFirstCellAsLabel ); + + // don't modify pChartListenerCollection here, called from there + return; + } + } + } + pObject = aIter.Next(); + } + } +} + + +BOOL ScDocument::HasData( SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + if (pTab[nTab]) + return pTab[nTab]->HasData( nCol, nRow ); + else + return FALSE; +} + +uno::Reference< embed::XEmbeddedObject > + ScDocument::FindOleObjectByName( const String& rName ) +{ + if (!pDrawLayer) + return uno::Reference< embed::XEmbeddedObject >(); + + // die Seiten hier vom Draw-Layer nehmen, + // weil sie evtl. nicht mit den Tabellen uebereinstimmen + // (z.B. Redo von Tabelle loeschen, Draw-Redo passiert vor DeleteTab). + + sal_uInt16 nCount = pDrawLayer->GetPageCount(); + for (sal_uInt16 nTab=0; nTab<nCount; nTab++) + { + SdrPage* pPage = pDrawLayer->GetPage(nTab); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 ) + { + SdrOle2Obj * pOleObject ( dynamic_cast< SdrOle2Obj * >( pObject )); + if( pOleObject && + pOleObject->GetPersistName() == rName ) + { + return pOleObject->GetObjRef(); + } + } + pObject = aIter.Next(); + } + } + + return uno::Reference< embed::XEmbeddedObject >(); +} + +BOOL lcl_StringInCollection( const ScStrCollection* pColl, const String& rStr ) +{ + if ( !pColl ) + return FALSE; + + StrData aData( rStr ); + USHORT nDummy; + return pColl->Search( &aData, nDummy ); +} + +void ScDocument::UpdateChartListenerCollection() +{ + bChartListenerCollectionNeedsUpdate = FALSE; + if (!pDrawLayer) + return; + else + { + ScRange aRange; + // Range fuer Suche unwichtig + ScChartListener aCLSearcher( EMPTY_STRING, this, aRange ); + for (SCTAB nTab=0; nTab<=MAXTAB; nTab++) + { + if (pTab[nTab]) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 ) + { + String aObjName = ((SdrOle2Obj*)pObject)->GetPersistName(); + aCLSearcher.SetString( aObjName ); + USHORT nIndex; + if ( pChartListenerCollection->Search( &aCLSearcher, nIndex ) ) + { + ((ScChartListener*) (pChartListenerCollection-> + At( nIndex )))->SetUsed( TRUE ); + } + else if ( lcl_StringInCollection( pOtherObjects, aObjName ) ) + { + // non-chart OLE object -> don't touch + } + else + { + bool bIsChart = false; + + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pObject)->GetObjRef(); + DBG_ASSERT( xIPObj.is(), "No embedded object is given!"); + uno::Reference< ::com::sun::star::chart2::data::XDataReceiver > xReceiver; + uno::Reference< embed::XComponentSupplier > xCompSupp( xIPObj, uno::UNO_QUERY ); + if( xCompSupp.is()) + xReceiver.set( xCompSupp->getComponent(), uno::UNO_QUERY ); + + // if the object is a chart2::XDataReceiver, we must attach as XDataProvider + if( xReceiver.is() && + !PastingDrawFromOtherDoc()) + { + // NOTE: this currently does not work as we are + // unable to set the data. So a chart from the + // same document is treated like a chart with + // own data for the time being. +#if 0 + // data provider + uno::Reference< chart2::data::XDataProvider > xDataProvider = new + ScChart2DataProvider( this ); + xReceiver->attachDataProvider( xDataProvider ); + // number formats supplier + uno::Reference< util::XNumberFormatsSupplier > xNumberFormatsSupplier( pShell->GetModel(), uno::UNO_QUERY ); + xReceiver->attachNumberFormatsSupplier( xNumberFormatsSupplier ); + // data ? + // how to set?? Defined in XML-file, which is already loaded!!! + // => we have to do this stuff here, BEFORE the chart is actually loaded + + bIsChart = true; +#endif + } + + if (!bIsChart) + { + // put into list of other ole objects, so the object doesn't have to + // be swapped in the next time UpdateChartListenerCollection is called + //! remove names when objects are no longer there? + // (object names aren't used again before reloading the document) + + if (!pOtherObjects) + pOtherObjects = new ScStrCollection; + pOtherObjects->Insert( new StrData( aObjName ) ); + } + } + } + pObject = aIter.Next(); + } + } + } + // alle nicht auf SetUsed gesetzten loeschen + pChartListenerCollection->FreeUnused(); + } +} + +void ScDocument::AddOLEObjectToCollection(const String& rName) +{ + if (!pOtherObjects) + pOtherObjects = new ScStrCollection; + pOtherObjects->Insert( new StrData( rName ) ); +} + + + diff --git a/sc/source/core/data/documen6.cxx b/sc/source/core/data/documen6.cxx new file mode 100644 index 000000000000..68c5655bb6c4 --- /dev/null +++ b/sc/source/core/data/documen6.cxx @@ -0,0 +1,187 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen6.cxx,v $ + * $Revision: 1.15 $ + * + * 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 "scitems.hxx" +#include <svx/scripttypeitem.hxx> + +#include <com/sun/star/i18n/XBreakIterator.hpp> +#include <com/sun/star/i18n/ScriptType.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> + +#include "document.hxx" +#include "cell.hxx" +#include "cellform.hxx" +#include "patattr.hxx" +#include "scrdata.hxx" +#include "poolhelp.hxx" + +using namespace com::sun::star; + +#define SC_BREAKITER_SERVICE "com.sun.star.i18n.BreakIterator" + +// +// this file is compiled with exceptions enabled +// put functions here that need exceptions! +// + +// ----------------------------------------------------------------------- + +const uno::Reference< i18n::XBreakIterator >& ScDocument::GetBreakIterator() +{ + if ( !pScriptTypeData ) + pScriptTypeData = new ScScriptTypeData; + if ( !pScriptTypeData->xBreakIter.is() ) + { + uno::Reference< uno::XInterface > xInterface = xServiceManager->createInstance( + ::rtl::OUString::createFromAscii( SC_BREAKITER_SERVICE ) ); + pScriptTypeData->xBreakIter = uno::Reference< i18n::XBreakIterator >( xInterface, uno::UNO_QUERY ); + DBG_ASSERT( pScriptTypeData->xBreakIter.is(), "can't get BreakIterator" ); + } + return pScriptTypeData->xBreakIter; +} + +BOOL ScDocument::HasStringWeakCharacters( const String& rString ) +{ + if (rString.Len()) + { + uno::Reference<i18n::XBreakIterator> xBreakIter = GetBreakIterator(); + if ( xBreakIter.is() ) + { + rtl::OUString aText = rString; + sal_Int32 nLen = aText.getLength(); + + sal_Int32 nPos = 0; + do + { + sal_Int16 nType = xBreakIter->getScriptType( aText, nPos ); + if ( nType == i18n::ScriptType::WEAK ) + return TRUE; // found + + nPos = xBreakIter->endOfScript( aText, nPos, nType ); + } + while ( nPos >= 0 && nPos < nLen ); + } + } + + return FALSE; // none found +} + +BYTE ScDocument::GetStringScriptType( const String& rString ) +{ + + BYTE nRet = 0; + if (rString.Len()) + { + uno::Reference<i18n::XBreakIterator> xBreakIter = GetBreakIterator(); + if ( xBreakIter.is() ) + { + rtl::OUString aText = rString; + sal_Int32 nLen = aText.getLength(); + + sal_Int32 nPos = 0; + do + { + sal_Int16 nType = xBreakIter->getScriptType( aText, nPos ); + switch ( nType ) + { + case i18n::ScriptType::LATIN: + nRet |= SCRIPTTYPE_LATIN; + break; + case i18n::ScriptType::ASIAN: + nRet |= SCRIPTTYPE_ASIAN; + break; + case i18n::ScriptType::COMPLEX: + nRet |= SCRIPTTYPE_COMPLEX; + break; + // WEAK is ignored + } + nPos = xBreakIter->endOfScript( aText, nPos, nType ); + } + while ( nPos >= 0 && nPos < nLen ); + } + } + return nRet; +} + +BYTE ScDocument::GetCellScriptType( ScBaseCell* pCell, ULONG nNumberFormat ) +{ + if ( !pCell ) + return 0; // empty + + BYTE nStored = pCell->GetScriptType(); + if ( nStored != SC_SCRIPTTYPE_UNKNOWN ) // stored value valid? + return nStored; // use stored value + + String aStr; + Color* pColor; + ScCellFormat::GetString( pCell, nNumberFormat, aStr, &pColor, *xPoolHelper->GetFormTable() ); + + BYTE nRet = GetStringScriptType( aStr ); + + pCell->SetScriptType( nRet ); // store for later calls + + return nRet; +} + +BYTE ScDocument::GetScriptType( SCCOL nCol, SCROW nRow, SCTAB nTab, ScBaseCell* pCell ) +{ + // if cell is not passed, take from document + + if (!pCell) + { + pCell = GetCell( ScAddress( nCol, nRow, nTab ) ); + if ( !pCell ) + return 0; // empty + } + + // if script type is set, don't have to get number formats + + BYTE nStored = pCell->GetScriptType(); + if ( nStored != SC_SCRIPTTYPE_UNKNOWN ) // stored value valid? + return nStored; // use stored value + + // include number formats from conditional formatting + + const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab ); + if (!pPattern) return 0; + const SfxItemSet* pCondSet = NULL; + if ( ((const SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ) + pCondSet = GetCondResult( nCol, nRow, nTab ); + + ULONG nFormat = pPattern->GetNumberFormat( xPoolHelper->GetFormTable(), pCondSet ); + return GetCellScriptType( pCell, nFormat ); +} + + diff --git a/sc/source/core/data/documen7.cxx b/sc/source/core/data/documen7.cxx new file mode 100644 index 000000000000..9527089ebde3 --- /dev/null +++ b/sc/source/core/data/documen7.cxx @@ -0,0 +1,531 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen7.cxx,v $ + * $Revision: 1.12 $ + * + * 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 <vcl/svapp.hxx> + +#if defined( WNT ) && defined( erBEEP ) +#include <svwin.h> +#define erBEEPER() Beep( 666, 66 ) +#else +#define erBEEPER() +#endif + +#include "document.hxx" +#include "brdcst.hxx" +#include "bcaslot.hxx" +#include "cell.hxx" +#include "formula/errorcodes.hxx" // errCircularReference +#include "scerrors.hxx" +#include "docoptio.hxx" +#include "refupdat.hxx" +#include "table.hxx" +#include "progress.hxx" +#include "scmod.hxx" // SC_MOD +#include "inputopt.hxx" // GetExpandRefs +#include "conditio.hxx" +#include <tools/shl.hxx> + + +#include "globstr.hrc" + +extern const ScFormulaCell* pLastFormulaTreeTop; // cellform.cxx Err527 WorkAround + +// STATIC DATA ----------------------------------------------------------- + +#ifdef erDEBUG +ULONG erCountBCAInserts = 0; +ULONG erCountBCAFinds = 0; +#endif + +// ----------------------------------------------------------------------- + +void ScDocument::StartListeningArea( const ScRange& rRange, + SvtListener* pListener + ) +{ + if ( pBASM ) + pBASM->StartListeningArea( rRange, pListener ); +} + + +void ScDocument::EndListeningArea( const ScRange& rRange, + SvtListener* pListener + ) +{ + if ( pBASM ) + pBASM->EndListeningArea( rRange, pListener ); +} + + +void ScDocument::Broadcast( ULONG nHint, const ScAddress& rAddr, + ScBaseCell* pCell + ) +{ + if ( !pBASM ) + return ; // Clipboard or Undo + ScHint aHint( nHint, rAddr, pCell ); + Broadcast( aHint ); +} + + +void ScDocument::Broadcast( const ScHint& rHint ) +{ + if ( !pBASM ) + return ; // Clipboard or Undo + if ( !nHardRecalcState ) + { + ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast + BOOL bIsBroadcasted = FALSE; + ScBaseCell* pCell = rHint.GetCell(); + if ( pCell ) + { + SvtBroadcaster* pBC = pCell->GetBroadcaster(); + if ( pBC ) + { + pBC->Broadcast( rHint ); + bIsBroadcasted = TRUE; + } + } + if ( pBASM->AreaBroadcast( rHint ) || bIsBroadcasted ) + TrackFormulas( rHint.GetId() ); + } + + // Repaint fuer bedingte Formate mit relativen Referenzen: + if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS ) + pCondFormList->SourceChanged( rHint.GetAddress() ); + + if ( rHint.GetAddress() != BCA_BRDCST_ALWAYS ) + { + SCTAB nTab = rHint.GetAddress().Tab(); + if (pTab[nTab] && pTab[nTab]->IsStreamValid()) + pTab[nTab]->SetStreamValid(FALSE); + } +} + + +void ScDocument::AreaBroadcast( const ScHint& rHint ) +{ + if ( !pBASM ) + return ; // Clipboard or Undo + if ( !nHardRecalcState ) + { + ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast + if ( pBASM->AreaBroadcast( rHint ) ) + TrackFormulas( rHint.GetId() ); + } + + // Repaint fuer bedingte Formate mit relativen Referenzen: + if ( pCondFormList && rHint.GetAddress() != BCA_BRDCST_ALWAYS ) + pCondFormList->SourceChanged( rHint.GetAddress() ); +} + + +void ScDocument::AreaBroadcastInRange( const ScRange& rRange, const ScHint& rHint ) +{ + if ( !pBASM ) + return ; // Clipboard or Undo + if ( !nHardRecalcState ) + { + ScBulkBroadcast aBulkBroadcast( pBASM); // scoped bulk broadcast + if ( pBASM->AreaBroadcastInRange( rRange, rHint ) ) + TrackFormulas( rHint.GetId() ); + } + + // Repaint for conditional formats containing relative references. + //! This is _THE_ bottle neck! + if ( pCondFormList ) + { + SCCOL nCol; + SCROW nRow; + SCTAB nTab; + SCCOL nCol1; + SCROW nRow1; + SCTAB nTab1; + SCCOL nCol2; + SCROW nRow2; + SCTAB nTab2; + rRange.GetVars( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + ScAddress aAddress( rRange.aStart ); + for ( nTab = nTab1; nTab <= nTab2; ++nTab ) + { + aAddress.SetTab( nTab ); + for ( nCol = nCol1; nCol <= nCol2; ++nCol ) + { + aAddress.SetCol( nCol ); + for ( nRow = nRow1; nRow <= nRow2; ++nRow ) + { + aAddress.SetRow( nRow ); + pCondFormList->SourceChanged( aAddress ); + } + } + } + } +} + + +void ScDocument::DelBroadcastAreasInRange( const ScRange& rRange ) +{ + if ( pBASM ) + pBASM->DelBroadcastAreasInRange( rRange ); +} + +void ScDocument::StartListeningCell( const ScAddress& rAddress, + SvtListener* pListener ) +{ + DBG_ASSERT(pListener, "StartListeningCell: pListener Null"); + SCTAB nTab = rAddress.Tab(); + if (pTab[nTab]) + pTab[nTab]->StartListening( rAddress, pListener ); +} + +void ScDocument::EndListeningCell( const ScAddress& rAddress, + SvtListener* pListener ) +{ + DBG_ASSERT(pListener, "EndListeningCell: pListener Null"); + SCTAB nTab = rAddress.Tab(); + if (pTab[nTab]) + pTab[nTab]->EndListening( rAddress, pListener ); +} + + +void ScDocument::PutInFormulaTree( ScFormulaCell* pCell ) +{ + DBG_ASSERT( pCell, "PutInFormulaTree: pCell Null" ); + RemoveFromFormulaTree( pCell ); + // anhaengen + if ( pEOFormulaTree ) + pEOFormulaTree->SetNext( pCell ); + else + pFormulaTree = pCell; // kein Ende, kein Anfang.. + pCell->SetPrevious( pEOFormulaTree ); + pCell->SetNext( 0 ); + pEOFormulaTree = pCell; + nFormulaCodeInTree += pCell->GetCode()->GetCodeLen(); +} + + +void ScDocument::RemoveFromFormulaTree( ScFormulaCell* pCell ) +{ + DBG_ASSERT( pCell, "RemoveFromFormulaTree: pCell Null" ); + ScFormulaCell* pPrev = pCell->GetPrevious(); + // wenn die Zelle die erste oder sonstwo ist + if ( pPrev || pFormulaTree == pCell ) + { + ScFormulaCell* pNext = pCell->GetNext(); + if ( pPrev ) + pPrev->SetNext( pNext ); // gibt Vorlaeufer + else + pFormulaTree = pNext; // ist erste Zelle + if ( pNext ) + pNext->SetPrevious( pPrev ); // gibt Nachfolger + else + pEOFormulaTree = pPrev; // ist letzte Zelle + pCell->SetPrevious( 0 ); + pCell->SetNext( 0 ); + USHORT nRPN = pCell->GetCode()->GetCodeLen(); + if ( nFormulaCodeInTree >= nRPN ) + nFormulaCodeInTree -= nRPN; + else + { + DBG_ERRORFILE( "RemoveFromFormulaTree: nFormulaCodeInTree < nRPN" ); + nFormulaCodeInTree = 0; + } + } + else if ( !pFormulaTree && nFormulaCodeInTree ) + { + DBG_ERRORFILE( "!pFormulaTree && nFormulaCodeInTree != 0" ); + nFormulaCodeInTree = 0; + } +} + + +BOOL ScDocument::IsInFormulaTree( ScFormulaCell* pCell ) const +{ + return pCell->GetPrevious() || pFormulaTree == pCell; +} + + +void ScDocument::CalcFormulaTree( BOOL bOnlyForced, BOOL bNoProgress ) +{ + DBG_ASSERT( !IsCalculatingFormulaTree(), "CalcFormulaTree recursion" ); + // never ever recurse into this, might end up lost in infinity + if ( IsCalculatingFormulaTree() ) + return ; + bCalculatingFormulaTree = TRUE; + + SetForcedFormulaPending( FALSE ); + BOOL bOldIdleDisabled = IsIdleDisabled(); + DisableIdle( TRUE ); + BOOL bOldAutoCalc = GetAutoCalc(); + //! _nicht_ SetAutoCalc( TRUE ) weil das evtl. CalcFormulaTree( TRUE ) + //! aufruft, wenn vorher disabled war und bHasForcedFormulas gesetzt ist + bAutoCalc = TRUE; + if ( nHardRecalcState ) + CalcAll(); + else + { + ScFormulaCell* pCell = pFormulaTree; + while ( pCell ) + { + if ( pCell->GetDirty() ) + pCell = pCell->GetNext(); // alles klar + else + { + if ( pCell->GetCode()->IsRecalcModeAlways() ) + { + // pCell wird im SetDirty neu angehaengt! + ScFormulaCell* pNext = pCell->GetNext(); + pCell->SetDirty(); + // falls pNext==0 und neue abhaengige hinten angehaengt + // wurden, so macht das nichts, da die alle bDirty sind + pCell = pNext; + } + else + { // andere simpel berechnen + pCell->SetDirtyVar(); + pCell = pCell->GetNext(); + } + } + } + BOOL bProgress = !bOnlyForced && nFormulaCodeInTree && !bNoProgress; + if ( bProgress ) + ScProgress::CreateInterpretProgress( this, TRUE ); + + pCell = pFormulaTree; + ScFormulaCell* pLastNoGood = 0; + while ( pCell ) + { + // Interpret setzt bDirty zurueck und callt Remove, auch der referierten! + // bei RECALCMODE_ALWAYS bleibt die Zelle + if ( bOnlyForced ) + { + if ( pCell->GetCode()->IsRecalcModeForced() ) + pCell->Interpret(); + } + else + { + pCell->Interpret(); + } + if ( pCell->GetPrevious() || pCell == pFormulaTree ) + { // (IsInFormulaTree(pCell)) kein Remove gewesen => next + pLastNoGood = pCell; + pCell = pCell->GetNext(); + } + else + { + if ( pFormulaTree ) + { + if ( pFormulaTree->GetDirty() && !bOnlyForced ) + { + pCell = pFormulaTree; + pLastNoGood = 0; + } + else + { + // IsInFormulaTree(pLastNoGood) + if ( pLastNoGood && (pLastNoGood->GetPrevious() || + pLastNoGood == pFormulaTree) ) + pCell = pLastNoGood->GetNext(); + else + { + pCell = pFormulaTree; + while ( pCell && !pCell->GetDirty() ) + pCell = pCell->GetNext(); + if ( pCell ) + pLastNoGood = pCell->GetPrevious(); + } + } + } + else + pCell = 0; + } + if ( ScProgress::IsUserBreak() ) + pCell = 0; + } + if ( bProgress ) + ScProgress::DeleteInterpretProgress(); + } + bAutoCalc = bOldAutoCalc; + DisableIdle( bOldIdleDisabled ); + bCalculatingFormulaTree = FALSE; +} + + +void ScDocument::ClearFormulaTree() +{ + ScFormulaCell* pCell; + ScFormulaCell* pTree = pFormulaTree; + while ( pTree ) + { + pCell = pTree; + pTree = pCell->GetNext(); + if ( !pCell->GetCode()->IsRecalcModeAlways() ) + RemoveFromFormulaTree( pCell ); + } +} + + +void ScDocument::AppendToFormulaTrack( ScFormulaCell* pCell ) +{ + DBG_ASSERT( pCell, "AppendToFormulaTrack: pCell Null" ); + // Zelle kann nicht in beiden Listen gleichzeitig sein + RemoveFromFormulaTrack( pCell ); + RemoveFromFormulaTree( pCell ); + if ( pEOFormulaTrack ) + pEOFormulaTrack->SetNextTrack( pCell ); + else + pFormulaTrack = pCell; // kein Ende, kein Anfang.. + pCell->SetPreviousTrack( pEOFormulaTrack ); + pCell->SetNextTrack( 0 ); + pEOFormulaTrack = pCell; + ++nFormulaTrackCount; +} + + +void ScDocument::RemoveFromFormulaTrack( ScFormulaCell* pCell ) +{ + DBG_ASSERT( pCell, "RemoveFromFormulaTrack: pCell Null" ); + ScFormulaCell* pPrev = pCell->GetPreviousTrack(); + // wenn die Zelle die erste oder sonstwo ist + if ( pPrev || pFormulaTrack == pCell ) + { + ScFormulaCell* pNext = pCell->GetNextTrack(); + if ( pPrev ) + pPrev->SetNextTrack( pNext ); // gibt Vorlaeufer + else + pFormulaTrack = pNext; // ist erste Zelle + if ( pNext ) + pNext->SetPreviousTrack( pPrev ); // gibt Nachfolger + else + pEOFormulaTrack = pPrev; // ist letzte Zelle + pCell->SetPreviousTrack( 0 ); + pCell->SetNextTrack( 0 ); + --nFormulaTrackCount; + } +} + + +BOOL ScDocument::IsInFormulaTrack( ScFormulaCell* pCell ) const +{ + return pCell->GetPreviousTrack() || pFormulaTrack == pCell; +} + + +/* + Der erste wird gebroadcastet, + die dadurch entstehenden werden durch das Notify an den Track gehaengt. + Der nachfolgende broadcastet wieder usw. + View stoesst Interpret an. + */ +void ScDocument::TrackFormulas( ULONG nHintId ) +{ + + if ( pFormulaTrack ) + { + erBEEPER(); + SvtBroadcaster* pBC; + ScFormulaCell* pTrack; + ScFormulaCell* pNext; + pTrack = pFormulaTrack; + do + { + ScHint aHint( nHintId, pTrack->aPos, pTrack ); + if ( ( pBC = pTrack->GetBroadcaster() ) != NULL ) + pBC->Broadcast( aHint ); + pBASM->AreaBroadcast( aHint ); + // Repaint fuer bedingte Formate mit relativen Referenzen: + if ( pCondFormList ) + pCondFormList->SourceChanged( pTrack->aPos ); + pTrack = pTrack->GetNextTrack(); + } while ( pTrack ); + pTrack = pFormulaTrack; + BOOL bHaveForced = FALSE; + do + { + pNext = pTrack->GetNextTrack(); + RemoveFromFormulaTrack( pTrack ); + PutInFormulaTree( pTrack ); + if ( pTrack->GetCode()->IsRecalcModeForced() ) + bHaveForced = TRUE; + pTrack = pNext; + } while ( pTrack ); + if ( bHaveForced ) + { + SetForcedFormulas( TRUE ); + if ( bAutoCalc && !IsAutoCalcShellDisabled() && !IsInInterpreter() + && !IsCalculatingFormulaTree() ) + CalcFormulaTree( TRUE ); + else + SetForcedFormulaPending( TRUE ); + } + } + DBG_ASSERT( nFormulaTrackCount==0, "TrackFormulas: nFormulaTrackCount!=0" ); +} + + +void ScDocument::StartAllListeners() +{ + for ( SCTAB i = 0; i <= MAXTAB; ++i ) + if ( pTab[i] ) + pTab[i]->StartAllListeners(); +} + +void ScDocument::UpdateBroadcastAreas( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz + ) +{ + BOOL bExpandRefsOld = IsExpandRefs(); + if ( eUpdateRefMode == URM_INSDEL && (nDx > 0 || nDy > 0 || nDz > 0) ) + SetExpandRefs( SC_MOD()->GetInputOptions().GetExpandRefs() ); + if ( pBASM ) + pBASM->UpdateBroadcastAreas( eUpdateRefMode, rRange, nDx, nDy, nDz ); + SetExpandRefs( bExpandRefsOld ); +} + +void ScDocument::SetAutoCalc( BOOL bNewAutoCalc ) +{ + BOOL bOld = bAutoCalc; + bAutoCalc = bNewAutoCalc; + if ( !bOld && bNewAutoCalc && bHasForcedFormulas ) + { + if ( IsAutoCalcShellDisabled() ) + SetForcedFormulaPending( TRUE ); + else if ( !IsInInterpreter() ) + CalcFormulaTree( TRUE ); + } +} + + + diff --git a/sc/source/core/data/documen8.cxx b/sc/source/core/data/documen8.cxx new file mode 100644 index 000000000000..64433a30297a --- /dev/null +++ b/sc/source/core/data/documen8.cxx @@ -0,0 +1,1630 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen8.cxx,v $ + * $Revision: 1.52.32.3 $ + * + * 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" + + +#define _ZFORLIST_DECLARE_TABLE +#include "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <tools/string.hxx> +#include <svx/editobj.hxx> +#include <svx/editstat.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/langitem.hxx> +#include <svx/linkmgr.hxx> +#include <svx/scripttypeitem.hxx> +#include <svx/unolingu.hxx> +#include <sfx2/bindings.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/printer.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <svtools/flagitem.hxx> +#include <svtools/intitem.hxx> +#define _SVSTDARR_USHORTS +#include <svtools/svstdarr.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/zformat.hxx> +#include <svtools/misccfg.hxx> +#include <sfx2/app.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <svtools/securityoptions.hxx> + +#include <vcl/virdev.hxx> +#include <vcl/msgbox.hxx> + +#include "inputopt.hxx" +#include "global.hxx" +#include "table.hxx" +#include "column.hxx" +#include "cell.hxx" +#include "poolhelp.hxx" +#include "docpool.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "docoptio.hxx" +#include "viewopti.hxx" +#include "scextopt.hxx" +#include "rechead.hxx" +#include "ddelink.hxx" +#include "scmatrix.hxx" +#include "arealink.hxx" +#include "dociter.hxx" +#include "patattr.hxx" +#include "hints.hxx" +#include "editutil.hxx" +#include "progress.hxx" +#include "document.hxx" +#include "chartlis.hxx" +#include "chartlock.hxx" +#include "refupdat.hxx" +#include "validat.hxx" // fuer HasMacroCalls +#include "markdata.hxx" +#include "scmod.hxx" +#include "printopt.hxx" +#include "externalrefmgr.hxx" +#include "globstr.hrc" +#include "sc.hrc" +#include "charthelper.hxx" + +#define GET_SCALEVALUE(set,id) ((const SfxUInt16Item&)(set.Get( id ))).GetValue() + +// states for online spelling in the visible range (0 is set initially) +#define VSPL_START 0 +#define VSPL_DONE 1 + + +// STATIC DATA ----------------------------------------------------------- + +//------------------------------------------------------------------------ + +void ScDocument::ImplCreateOptions() +{ + pDocOptions = new ScDocOptions(); + pViewOptions = new ScViewOptions(); +} + +//------------------------------------------------------------------------ + +void ScDocument::ImplDeleteOptions() +{ + delete pDocOptions; + delete pViewOptions; + delete pExtDocOptions; +} + +//------------------------------------------------------------------------ + +SfxPrinter* ScDocument::GetPrinter(BOOL bCreateIfNotExist) +{ + if ( !pPrinter && bCreateIfNotExist ) + { + SfxItemSet* pSet = + new SfxItemSet( *xPoolHelper->GetDocPool(), + SID_PRINTER_NOTFOUND_WARN, SID_PRINTER_NOTFOUND_WARN, + SID_PRINTER_CHANGESTODOC, SID_PRINTER_CHANGESTODOC, + SID_PRINT_SELECTEDSHEET, SID_PRINT_SELECTEDSHEET, + SID_SCPRINTOPTIONS, SID_SCPRINTOPTIONS, + NULL ); + + SfxMiscCfg* pOffCfg = SFX_APP()->GetMiscConfig(); + if ( pOffCfg ) + { + USHORT nFlags = 0; + if ( pOffCfg->IsPaperOrientationWarning() ) + nFlags |= SFX_PRINTER_CHG_ORIENTATION; + if ( pOffCfg->IsPaperSizeWarning() ) + nFlags |= SFX_PRINTER_CHG_SIZE; + pSet->Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, nFlags ) ); + pSet->Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, pOffCfg->IsNotFoundWarning() ) ); + } + + pPrinter = new SfxPrinter( pSet ); + pPrinter->SetMapMode( MAP_100TH_MM ); + UpdateDrawPrinter(); + pPrinter->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() ); + } + + return pPrinter; +} + +//------------------------------------------------------------------------ + +void ScDocument::SetPrinter( SfxPrinter* pNewPrinter ) +{ + if ( pNewPrinter == pPrinter ) + { + // #i6706# SetPrinter is called with the same printer again if + // the JobSetup has changed. In that case just call UpdateDrawPrinter + // (SetRefDevice for drawing layer) because of changed text sizes. + UpdateDrawPrinter(); + } + else + { + SfxPrinter* pOld = pPrinter; + pPrinter = pNewPrinter; + UpdateDrawPrinter(); + pPrinter->SetDigitLanguage( SC_MOD()->GetOptDigitLanguage() ); + delete pOld; + } + InvalidateTextWidth(NULL, NULL, FALSE); // in both cases +} + +//------------------------------------------------------------------------ + +void ScDocument::SetPrintOptions() +{ + if ( !pPrinter ) GetPrinter(); // setzt pPrinter + DBG_ASSERT( pPrinter, "Error in printer creation :-/" ); + + if ( pPrinter ) + { + SfxMiscCfg* pOffCfg = SFX_APP()->GetMiscConfig(); + if ( pOffCfg ) + { + SfxItemSet aOptSet( pPrinter->GetOptions() ); + + USHORT nFlags = 0; + if ( pOffCfg->IsPaperOrientationWarning() ) + nFlags |= SFX_PRINTER_CHG_ORIENTATION; + if ( pOffCfg->IsPaperSizeWarning() ) + nFlags |= SFX_PRINTER_CHG_SIZE; + aOptSet.Put( SfxFlagItem( SID_PRINTER_CHANGESTODOC, nFlags ) ); + aOptSet.Put( SfxBoolItem( SID_PRINTER_NOTFOUND_WARN, pOffCfg->IsNotFoundWarning() ) ); + + pPrinter->SetOptions( aOptSet ); + } + } +} + +//------------------------------------------------------------------------ + +VirtualDevice* ScDocument::GetVirtualDevice_100th_mm() +{ + if (!pVirtualDevice_100th_mm) + { +// pVirtualDevice_100th_mm = new VirtualDevice; +// pVirtualDevice_100th_mm->SetMapMode( MAP_100TH_MM ); + + pVirtualDevice_100th_mm = new VirtualDevice( 1 ); + pVirtualDevice_100th_mm->SetReferenceDevice(VirtualDevice::REFDEV_MODE_MSO1); + MapMode aMapMode( pVirtualDevice_100th_mm->GetMapMode() ); + aMapMode.SetMapUnit( MAP_100TH_MM ); + pVirtualDevice_100th_mm->SetMapMode( aMapMode ); + } + return pVirtualDevice_100th_mm; +} + +OutputDevice* ScDocument::GetRefDevice() +{ + // Create printer like ref device, see Writer... + OutputDevice* pRefDevice = NULL; + if ( SC_MOD()->GetInputOptions().GetTextWysiwyg() ) + pRefDevice = GetPrinter(); + else + pRefDevice = GetVirtualDevice_100th_mm(); + return pRefDevice; +} + +//------------------------------------------------------------------------ + +void ScDocument::ModifyStyleSheet( SfxStyleSheetBase& rStyleSheet, + const SfxItemSet& rChanges ) +{ + SfxItemSet& rSet = rStyleSheet.GetItemSet(); + + switch ( rStyleSheet.GetFamily() ) + { + case SFX_STYLE_FAMILY_PAGE: + { + const USHORT nOldScale = GET_SCALEVALUE(rSet,ATTR_PAGE_SCALE); + const USHORT nOldScaleToPages = GET_SCALEVALUE(rSet,ATTR_PAGE_SCALETOPAGES); + rSet.Put( rChanges ); + const USHORT nNewScale = GET_SCALEVALUE(rSet,ATTR_PAGE_SCALE); + const USHORT nNewScaleToPages = GET_SCALEVALUE(rSet,ATTR_PAGE_SCALETOPAGES); + + if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) ) + InvalidateTextWidth( rStyleSheet.GetName() ); + + if( SvtLanguageOptions().IsCTLFontEnabled() ) + { + const SfxPoolItem *pItem = NULL; + if( rChanges.GetItemState(ATTR_WRITINGDIR, TRUE, &pItem ) == SFX_ITEM_SET ) + ScChartHelper::DoUpdateAllCharts( this ); + } + } + break; + + case SFX_STYLE_FAMILY_PARA: + { + BOOL bNumFormatChanged; + if ( ScGlobal::CheckWidthInvalidate( bNumFormatChanged, + rSet, rChanges ) ) + InvalidateTextWidth( NULL, NULL, bNumFormatChanged ); + + for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab) + if (pTab[nTab] && pTab[nTab]->IsStreamValid()) + pTab[nTab]->SetStreamValid( FALSE ); + + ULONG nOldFormat = + ((const SfxUInt32Item*)&rSet.Get( + ATTR_VALUE_FORMAT ))->GetValue(); + ULONG nNewFormat = + ((const SfxUInt32Item*)&rChanges.Get( + ATTR_VALUE_FORMAT ))->GetValue(); + LanguageType eNewLang, eOldLang; + eNewLang = eOldLang = LANGUAGE_DONTKNOW; + if ( nNewFormat != nOldFormat ) + { + SvNumberFormatter* pFormatter = GetFormatTable(); + eOldLang = pFormatter->GetEntry( nOldFormat )->GetLanguage(); + eNewLang = pFormatter->GetEntry( nNewFormat )->GetLanguage(); + } + + // Bedeutung der Items in rChanges: + // Item gesetzt - Aenderung uebernehmen + // Dontcare - Default setzen + // Default - keine Aenderung + // ("keine Aenderung" geht nicht mit PutExtended, darum Schleife) + for (USHORT nWhich = ATTR_PATTERN_START; nWhich <= ATTR_PATTERN_END; nWhich++) + { + const SfxPoolItem* pItem; + SfxItemState eState = rChanges.GetItemState( nWhich, FALSE, &pItem ); + if ( eState == SFX_ITEM_SET ) + rSet.Put( *pItem ); + else if ( eState == SFX_ITEM_DONTCARE ) + rSet.ClearItem( nWhich ); + // bei Default nichts + } + + if ( eNewLang != eOldLang ) + rSet.Put( + SvxLanguageItem( eNewLang, ATTR_LANGUAGE_FORMAT ) ); + } + break; + default: + { + // added to avoid warnings + } + } +} + +//------------------------------------------------------------------------ + +void ScDocument::CopyStdStylesFrom( ScDocument* pSrcDoc ) +{ + // #b5017505# number format exchange list has to be handled here, too + NumFmtMergeHandler aNumFmtMergeHdl(this, pSrcDoc); + xPoolHelper->GetStylePool()->CopyStdStylesFrom( pSrcDoc->xPoolHelper->GetStylePool() ); +} + +//------------------------------------------------------------------------ + +void ScDocument::InvalidateTextWidth( const String& rStyleName ) +{ + const SCTAB nCount = GetTableCount(); + for ( SCTAB i=0; i<nCount && pTab[i]; i++ ) + if ( pTab[i]->GetPageStyle() == rStyleName ) + InvalidateTextWidth( i ); +} + +//------------------------------------------------------------------------ + +void ScDocument::InvalidateTextWidth( SCTAB nTab ) +{ + ScAddress aAdrFrom( 0, 0, nTab ); + ScAddress aAdrTo ( MAXCOL, MAXROW, nTab ); + InvalidateTextWidth( &aAdrFrom, &aAdrTo, FALSE ); +} + +//------------------------------------------------------------------------ + +BOOL ScDocument::IsPageStyleInUse( const String& rStrPageStyle, SCTAB* pInTab ) +{ + BOOL bInUse = FALSE; + const SCTAB nCount = GetTableCount(); + SCTAB i; + + for ( i = 0; !bInUse && i < nCount && pTab[i]; i++ ) + bInUse = ( pTab[i]->GetPageStyle() == rStrPageStyle ); + + if ( pInTab ) + *pInTab = i-1; + + return bInUse; +} + +//------------------------------------------------------------------------ + +BOOL ScDocument::RemovePageStyleInUse( const String& rStyle ) +{ + BOOL bWasInUse = FALSE; + const SCTAB nCount = GetTableCount(); + + for ( SCTAB i=0; i<nCount && pTab[i]; i++ ) + if ( pTab[i]->GetPageStyle() == rStyle ) + { + bWasInUse = TRUE; + pTab[i]->SetPageStyle( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ); + } + + return bWasInUse; +} + +BOOL ScDocument::RenamePageStyleInUse( const String& rOld, const String& rNew ) +{ + BOOL bWasInUse = FALSE; + const SCTAB nCount = GetTableCount(); + + for ( SCTAB i=0; i<nCount && pTab[i]; i++ ) + if ( pTab[i]->GetPageStyle() == rOld ) + { + bWasInUse = TRUE; + pTab[i]->SetPageStyle( rNew ); + } + + return bWasInUse; +} + +//------------------------------------------------------------------------ + +BYTE ScDocument::GetEditTextDirection(SCTAB nTab) const +{ + EEHorizontalTextDirection eRet = EE_HTEXTDIR_DEFAULT; + + String aStyleName = GetPageStyle( nTab ); + SfxStyleSheetBase* pStyle = xPoolHelper->GetStylePool()->Find( aStyleName, SFX_STYLE_FAMILY_PAGE ); + if ( pStyle ) + { + SfxItemSet& rStyleSet = pStyle->GetItemSet(); + SvxFrameDirection eDirection = (SvxFrameDirection) + ((const SvxFrameDirectionItem&)rStyleSet.Get( ATTR_WRITINGDIR )).GetValue(); + + if ( eDirection == FRMDIR_HORI_LEFT_TOP ) + eRet = EE_HTEXTDIR_L2R; + else if ( eDirection == FRMDIR_HORI_RIGHT_TOP ) + eRet = EE_HTEXTDIR_R2L; + // else (invalid for EditEngine): keep "default" + } + + return sal::static_int_cast<BYTE>(eRet); +} + +//------------------------------------------------------------------------ + +void ScDocument::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, + BOOL bNumFormatChanged ) +{ + BOOL bBroadcast = (bNumFormatChanged && GetDocOptions().IsCalcAsShown() && !IsImportingXML() && !IsClipboard()); + if ( pAdrFrom && !pAdrTo ) + { + const SCTAB nTab = pAdrFrom->Tab(); + + if ( pTab[nTab] ) + pTab[nTab]->InvalidateTextWidth( pAdrFrom, NULL, bNumFormatChanged, bBroadcast ); + } + else + { + const SCTAB nTabStart = pAdrFrom ? pAdrFrom->Tab() : 0; + const SCTAB nTabEnd = pAdrTo ? pAdrTo->Tab() : MAXTAB; + + for ( SCTAB nTab=nTabStart; nTab<=nTabEnd; nTab++ ) + if ( pTab[nTab] ) + pTab[nTab]->InvalidateTextWidth( pAdrFrom, pAdrTo, bNumFormatChanged, bBroadcast ); + } +} + +//------------------------------------------------------------------------ + +#define CALCMAX 1000 // Berechnungen +#define ABORT_EVENTS (INPUT_ANY & ~INPUT_TIMER & ~INPUT_OTHER) + +BOOL ScDocument::IdleCalcTextWidth() // TRUE = demnaechst wieder versuchen +{ + // #i75610# if a printer hasn't been set or created yet, don't create one for this + if ( bIdleDisabled || IsInLinkUpdate() || GetPrinter(FALSE) == NULL ) + return FALSE; + bIdleDisabled = TRUE; + +// ULONG nMs = 0; +// USHORT nIter = 0; + + const ULONG nStart = Time::GetSystemTicks(); + double nPPTX = 0.0; + double nPPTY = 0.0; + OutputDevice* pDev = NULL; + MapMode aOldMap; + ScStyleSheet* pStyle = NULL; + ScColumnIterator* pColIter = NULL; + ScTable* pTable = NULL; + ScColumn* pColumn = NULL; + ScBaseCell* pCell = NULL; + SCTAB nTab = aCurTextWidthCalcPos.Tab(); + SCROW nRow = aCurTextWidthCalcPos.Row(); + SCsCOL nCol = aCurTextWidthCalcPos.Col(); + USHORT nRestart = 0; + USHORT nZoom = 0; + BOOL bNeedMore= FALSE; + + if ( !ValidRow(nRow) ) + nRow = 0, nCol--; + if ( nCol < 0 ) + nCol = MAXCOL, nTab++; + if ( !ValidTab(nTab) || !pTab[nTab] ) + nTab = 0; + +// DBG_ERROR( String("Start = ") + String(nTab) + String(',') + String(nCol) + String(',') + String(nRow) ); + + // SearchMask/Family muss gemerkt werden, + // damit z.B. der Organizer nicht durcheinanderkommt, wenn zwischendurch eine + // Query-Box aufgemacht wird !!! + + ScStyleSheetPool* pStylePool = xPoolHelper->GetStylePool(); + USHORT nOldMask = pStylePool->GetSearchMask(); + SfxStyleFamily eOldFam = pStylePool->GetSearchFamily(); + + pTable = pTab[nTab]; + pStylePool->SetSearchMask( SFX_STYLE_FAMILY_PAGE, SFXSTYLEBIT_ALL ); + pStyle = (ScStyleSheet*)pStylePool->Find( pTable->aPageStyle, + SFX_STYLE_FAMILY_PAGE ); + + DBG_ASSERT( pStyle, "Missing StyleSheet :-/" ); + + BOOL bProgress = FALSE; + if ( pStyle && 0 == GET_SCALEVALUE(pStyle->GetItemSet(),ATTR_PAGE_SCALETOPAGES) ) + { + USHORT nCount = 0; + + nZoom = GET_SCALEVALUE(pStyle->GetItemSet(),ATTR_PAGE_SCALE); + Fraction aZoomFract( nZoom, 100 ); + pColumn = &pTable->aCol[nCol]; + pColIter = new ScColumnIterator( pColumn, nRow, MAXROW ); + + while ( (nZoom > 0) && (nCount < CALCMAX) && (nRestart < 2) ) + { + if ( pColIter->Next( nRow, pCell ) ) + { + if ( TEXTWIDTH_DIRTY == pCell->GetTextWidth() ) + { + if ( !pDev ) + { + pDev = GetPrinter(); + aOldMap = pDev->GetMapMode(); + pDev->SetMapMode( MAP_PIXEL ); // wichtig fuer GetNeededSize + + Point aPix1000 = pDev->LogicToPixel( Point(1000,1000), MAP_TWIP ); + nPPTX = aPix1000.X() / 1000.0; + nPPTY = aPix1000.Y() / 1000.0; + } + if ( !bProgress && pCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pCell)->GetDirty() ) + { + ScProgress::CreateInterpretProgress( this, FALSE ); + bProgress = TRUE; + } + +// DBG_ERROR( String("t,c,r = ") + String(nTab) + String(',') + String(nCol) + String(',') + String(nRow) ); +// DBG_ERROR( String("nOldWidth = ") + String(pCell->GetTextWidth()) ); + + USHORT nNewWidth = (USHORT)GetNeededSize( nCol, nRow, nTab, + pDev, nPPTX, nPPTY, + aZoomFract,aZoomFract, TRUE, + TRUE ); // bTotalSize + +// DBG_ERROR( String("nNewWidth = ") + String(nNewWidth) ); + + pCell->SetTextWidth( nNewWidth ); + + bNeedMore = TRUE; + } + } + else + { + BOOL bNewTab = FALSE; + + nRow = 0; + nCol--; + + if ( nCol < 0 ) + { + nCol = MAXCOL; + nTab++; + bNewTab = TRUE; + } + + if ( !ValidTab(nTab) || !pTab[nTab] ) + { + nTab = 0; + nRestart++; + bNewTab = TRUE; + } + + if ( nRestart < 2 ) + { + if ( bNewTab ) + { + pTable = pTab[nTab]; + pStyle = (ScStyleSheet*)pStylePool->Find( pTable->aPageStyle, + SFX_STYLE_FAMILY_PAGE ); + + if ( pStyle ) + { + SfxItemSet& rSet = pStyle->GetItemSet(); + if ( GET_SCALEVALUE( rSet, ATTR_PAGE_SCALETOPAGES ) == 0 ) + nZoom = GET_SCALEVALUE(rSet, ATTR_PAGE_SCALE ); + else + nZoom = 0; + } + else + { + DBG_ERROR( "Missing StyleSheet :-/" ); + } + } + + if ( nZoom > 0 ) + { + delete pColIter; + + pColumn = &pTable->aCol[nCol]; + pColIter = new ScColumnIterator( pColumn, nRow, MAXROW ); + } + else + nTab++; // Tabelle nicht mit absolutem Zoom -> naechste + } + } + +// nIter = nCount; + + nCount++; + + // Idle Berechnung abbrechen, wenn Berechnungen laenger als + // 50ms dauern, oder nach 32 Berechnungen mal nachschauen, ob + // bestimmte Events anstehen, die Beachtung wuenschen: + +// nMs = SysTicksToMs( GetSysTicks() - nStart ); + + if ( ( 50L < Time::GetSystemTicks() - nStart ) + || ( !(nCount&31) && Application::AnyInput( ABORT_EVENTS ) ) ) + nCount = CALCMAX; + } + } + else + nTab++; // Tabelle nicht mit absolutem Zoom -> naechste + + if ( bProgress ) + ScProgress::DeleteInterpretProgress(); + + delete pColIter; + +// DBG_ERROR( String(nCount) + String(" End = ") + String(nTab) + String(',') + String(nCol) + String(',') + String(nRow) ); + + if (pDev) + pDev->SetMapMode(aOldMap); + + aCurTextWidthCalcPos.SetTab( nTab ); + aCurTextWidthCalcPos.SetRow( nRow ); + aCurTextWidthCalcPos.SetCol( (SCCOL)nCol ); + +// DBG_ERROR( String(nMs) + String(" ms (") + String(nIter) + String(')') ); + + pStylePool->SetSearchMask( eOldFam, nOldMask ); + bIdleDisabled = FALSE; + + return bNeedMore; +} + +//------------------------------------------------------------------------ + +class ScSpellStatus +{ +public: + BOOL bModified; + + ScSpellStatus() : bModified(FALSE) {}; + + DECL_LINK (EventHdl, EditStatus*); +}; + +IMPL_LINK( ScSpellStatus, EventHdl, EditStatus *, pStatus ) +{ + ULONG nStatus = pStatus->GetStatusWord(); + if ( nStatus & EE_STAT_WRONGWORDCHANGED ) + bModified = TRUE; + + return 0; +} + +// SPELL_MAXCELLS muss mindestens 256 sein, solange am Iterator keine +// Start-Spalte gesetzt werden kann + +//! SPELL_MAXTEST fuer Timer und Idle unterschiedlich ??? + +// SPELL_MAXTEST now divided between visible and rest of document + +#define SPELL_MAXTEST_VIS 1 +#define SPELL_MAXTEST_ALL 3 +#define SPELL_MAXCELLS 256 + +BOOL ScDocument::OnlineSpellInRange( const ScRange& rSpellRange, ScAddress& rSpellPos, + USHORT nMaxTest ) +{ + ScEditEngineDefaulter* pEngine = NULL; //! am Dokument speichern + SfxItemSet* pDefaults = NULL; + ScSpellStatus aStatus; + + USHORT nCellCount = 0; // Zellen insgesamt + USHORT nTestCount = 0; // Aufrufe Spelling + BOOL bChanged = FALSE; // Aenderungen? + + SCCOL nCol = rSpellRange.aStart.Col(); // iterator always starts on the left edge + SCROW nRow = rSpellPos.Row(); + SCTAB nTab = rSpellPos.Tab(); + if ( !pTab[nTab] ) // sheet deleted? + { + nTab = rSpellRange.aStart.Tab(); + nRow = rSpellRange.aStart.Row(); + if ( !pTab[nTab] ) + { + // may happen for visible range + return FALSE; + } + } + ScHorizontalCellIterator aIter( this, nTab, + rSpellRange.aStart.Col(), nRow, + rSpellRange.aEnd.Col(), rSpellRange.aEnd.Row() ); + ScBaseCell* pCell = aIter.GetNext( nCol, nRow ); + // skip everything left of rSpellPos: + while ( pCell && nRow == rSpellPos.Row() && nCol < rSpellPos.Col() ) + pCell = aIter.GetNext( nCol, nRow ); + while ( pCell ) + { + CellType eType = pCell->GetCellType(); + if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT ) + { + if (!pEngine) + { + // #71154# ScTabEditEngine is needed + // because MapMode must be set for some old documents + pEngine = new ScTabEditEngine( this ); + pEngine->SetControlWord( pEngine->GetControlWord() | + ( EE_CNTRL_ONLINESPELLING | EE_CNTRL_ALLOWBIGOBJS ) ); + pEngine->SetStatusEventHdl( LINK( &aStatus, ScSpellStatus, EventHdl ) ); + // Delimiters hier wie in inputhdl.cxx !!! + pEngine->SetWordDelimiters( + ScEditUtil::ModifyDelimiters( pEngine->GetWordDelimiters() ) ); + pDefaults = new SfxItemSet( pEngine->GetEmptyItemSet() ); + + com::sun::star::uno::Reference<com::sun::star::linguistic2::XSpellChecker1> xXSpellChecker1( LinguMgr::GetSpellChecker() ); + + pEngine->SetSpeller( xXSpellChecker1 ); + } + + const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab ); + pPattern->FillEditItemSet( pDefaults ); + pEngine->SetDefaults( pDefaults, FALSE ); //! noetig ? + + USHORT nCellLang = ((const SvxLanguageItem&) + pPattern->GetItem(ATTR_FONT_LANGUAGE)).GetValue(); + if ( nCellLang == LANGUAGE_SYSTEM ) + nCellLang = Application::GetSettings().GetLanguage(); // never use SYSTEM for spelling + pEngine->SetDefaultLanguage( nCellLang ); + + if ( eType == CELLTYPE_STRING ) + { + String aText; + ((ScStringCell*)pCell)->GetString(aText); + pEngine->SetText( aText ); + } + else + pEngine->SetText( *((ScEditCell*)pCell)->GetData() ); + + aStatus.bModified = FALSE; + pEngine->CompleteOnlineSpelling(); + if ( aStatus.bModified ) // Fehler dazu oder weggekommen? + { + BOOL bNeedEdit = TRUE; // Test auf einfachen Text + if ( !pEngine->HasOnlineSpellErrors() ) + { + ScEditAttrTester aTester( pEngine ); + bNeedEdit = aTester.NeedsObject(); + } + + if ( bNeedEdit ) + { + EditTextObject* pNewData = pEngine->CreateTextObject(); + if ( eType == CELLTYPE_EDIT ) + ((ScEditCell*)pCell)->SetData( pNewData, + pEngine->GetEditTextObjectPool() ); + else + PutCell( nCol, nRow, nTab, new ScEditCell( pNewData, + this, pEngine->GetEditTextObjectPool() ) ); + delete pNewData; + } + else // einfacher String + PutCell( nCol, nRow, nTab, new ScStringCell( pEngine->GetText() ) ); + + // Paint + if (pShell) + { + // #47751# Seitenvorschau ist davon nicht betroffen + // (sollte jedenfalls nicht) + ScPaintHint aHint( ScRange( nCol, nRow, nTab ), PAINT_GRID ); + aHint.SetPrintFlag( FALSE ); + pShell->Broadcast( aHint ); + } + + bChanged = TRUE; + } + + if ( ++nTestCount >= nMaxTest ) // checked enough text? + break; + } + + if ( ++nCellCount >= SPELL_MAXCELLS ) // seen enough cells? + break; + + pCell = aIter.GetNext( nCol, nRow ); + } + + if ( pCell ) + { + ++nCol; // continue after last cell + if ( nCol > rSpellRange.aEnd.Col() ) + { + nCol = rSpellRange.aStart.Col(); + ++nRow; + if ( nRow > rSpellRange.aEnd.Row() ) + pCell = NULL; + } + } + + if (!pCell) // end of range reached -> next sheet + { + ++nTab; + if ( nTab > rSpellRange.aEnd.Tab() || !pTab[nTab] ) + nTab = rSpellRange.aStart.Tab(); + nCol = rSpellRange.aStart.Col(); + nRow = rSpellRange.aStart.Row(); + + nVisSpellState = VSPL_DONE; //! only if this is for the visible range + } + rSpellPos.Set( nCol, nRow, nTab ); + + delete pDefaults; + delete pEngine; // bevor aStatus out of scope geht + + return bChanged; +} + + +BOOL ScDocument::ContinueOnlineSpelling() +{ + if ( bIdleDisabled || !pDocOptions->IsAutoSpell() || (pShell && pShell->IsReadOnly()) ) + return FALSE; + + // #i48433# set bInsertingFromOtherDoc flag so there are no broadcasts when PutCell is called + // (same behavior as in RemoveAutoSpellObj: just transfer the broadcaster) + BOOL bOldInserting = IsInsertingFromOtherDoc(); + SetInsertingFromOtherDoc( TRUE ); + + //! use one EditEngine for both calls + + // #41504# first check visible range + BOOL bResult = OnlineSpellInRange( aVisSpellRange, aVisSpellPos, SPELL_MAXTEST_VIS ); + + // during first pass through visible range, always continue + if ( nVisSpellState == VSPL_START ) + bResult = TRUE; + + if (bResult) + { + // if errors found, continue there + OnlineSpellInRange( aVisSpellRange, aVisSpellPos, SPELL_MAXTEST_ALL ); + } + else + { + // if nothing found there, continue with rest of document + ScRange aTotalRange( 0,0,0, MAXCOL,MAXROW,MAXTAB ); + bResult = OnlineSpellInRange( aTotalRange, aOnlineSpellPos, SPELL_MAXTEST_ALL ); + } + + SetInsertingFromOtherDoc( bOldInserting ); + + return bResult; +} + + +void ScDocument::SetOnlineSpellPos( const ScAddress& rPos ) +{ + aOnlineSpellPos = rPos; + + // skip visible area for aOnlineSpellPos + if ( aVisSpellRange.In( aOnlineSpellPos ) ) + aOnlineSpellPos = aVisSpellRange.aEnd; +} + +BOOL ScDocument::SetVisibleSpellRange( const ScRange& rNewRange ) +{ + BOOL bChange = ( aVisSpellRange != rNewRange ); + if (bChange) + { + // continue spelling through visible range when scrolling down + BOOL bContDown = ( nVisSpellState == VSPL_START && rNewRange.In( aVisSpellPos ) && + rNewRange.aStart.Row() > aVisSpellRange.aStart.Row() && + rNewRange.aStart.Col() == aVisSpellRange.aStart.Col() && + rNewRange.aEnd.Col() == aVisSpellRange.aEnd.Col() ); + + aVisSpellRange = rNewRange; + + if ( !bContDown ) + { + aVisSpellPos = aVisSpellRange.aStart; + nVisSpellState = VSPL_START; + } + + // skip visible area for aOnlineSpellPos + if ( aVisSpellRange.In( aOnlineSpellPos ) ) + aOnlineSpellPos = aVisSpellRange.aEnd; + } + return bChange; +} + +void ScDocument::RemoveAutoSpellObj() +{ + // alle Spelling-Informationen entfernen + + for (SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) + pTab[nTab]->RemoveAutoSpellObj(); +} + +//------------------------------------------------------------------------ + +BOOL ScDocument::IdleCheckLinks() // TRUE = demnaechst wieder versuchen +{ + BOOL bAnyLeft = FALSE; + + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + { + ScDdeLink* pDdeLink = (ScDdeLink*)pBase; + if (pDdeLink->NeedsUpdate()) + { + pDdeLink->TryUpdate(); + if (pDdeLink->NeedsUpdate()) // war nix? + bAnyLeft = TRUE; + } + } + } + } + + return bAnyLeft; +} + +void ScDocument::SaveDdeLinks(SvStream& rStream) const +{ + // bei 4.0-Export alle mit Modus != DEFAULT weglassen + BOOL bExport40 = ( rStream.GetVersion() <= SOFFICE_FILEFORMAT_40 ); + + const ::sfx2::SvBaseLinks& rLinks = GetLinkManager()->GetLinks(); + USHORT nCount = rLinks.Count(); + + // erstmal zaehlen... + + USHORT nDdeCount = 0; + USHORT i; + for (i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + if ( !bExport40 || ((ScDdeLink*)pBase)->GetMode() == SC_DDE_DEFAULT ) + ++nDdeCount; + } + + // Header + + ScMultipleWriteHeader aHdr( rStream ); + rStream << nDdeCount; + + // Links speichern + + for (i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + { + ScDdeLink* pLink = (ScDdeLink*)pBase; + if ( !bExport40 || pLink->GetMode() == SC_DDE_DEFAULT ) + pLink->Store( rStream, aHdr ); + } + } +} + +void ScDocument::LoadDdeLinks(SvStream& rStream) +{ + ScMultipleReadHeader aHdr( rStream ); + + GetLinkManager(); + USHORT nCount; + rStream >> nCount; + for (USHORT i=0; i<nCount; i++) + { + ScDdeLink* pLink = new ScDdeLink( this, rStream, aHdr ); + pLinkManager->InsertDDELink( pLink, + pLink->GetAppl(), pLink->GetTopic(), pLink->GetItem() ); + } +} + +BOOL ScDocument::HasDdeLinks() const +{ + if (GetLinkManager()) // Clipboard z.B. hat keinen LinkManager + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + if ((*rLinks[i])->ISA(ScDdeLink)) + return TRUE; + } + + return FALSE; +} + +void ScDocument::SetInLinkUpdate(BOOL bSet) +{ + // called from TableLink and AreaLink + + DBG_ASSERT( bInLinkUpdate != bSet, "SetInLinkUpdate twice" ); + bInLinkUpdate = bSet; +} + +BOOL ScDocument::IsInLinkUpdate() const +{ + return bInLinkUpdate || IsInDdeLinkUpdate(); +} + +void ScDocument::UpdateExternalRefLinks() +{ + if (!GetLinkManager()) + return; + + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + + bool bAny = false; + for (USHORT i = 0; i < nCount; ++i) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + ScExternalRefLink* pRefLink = dynamic_cast<ScExternalRefLink*>(pBase); + if (pRefLink) + { + pRefLink->Update(); + bAny = true; + } + } + if (bAny) + { + TrackFormulas(); + pShell->Broadcast( SfxSimpleHint(FID_DATACHANGED) ); + ResetChanged( ScRange(0, 0, 0, MAXCOL, MAXROW, MAXTAB) ); + + // #i101960# set document modified, as in TrackTimeHdl for DDE links + if (!pShell->IsModified()) + { + pShell->SetModified( TRUE ); + SfxBindings* pBindings = GetViewBindings(); + if (pBindings) + { + pBindings->Invalidate( SID_SAVEDOC ); + pBindings->Invalidate( SID_DOC_MODIFIED ); + } + } + } +} + +void ScDocument::UpdateDdeLinks() +{ + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + USHORT i; + + // #49226# falls das Updaten laenger dauert, erstmal alle Werte + // zuruecksetzen, damit nichts altes (falsches) stehen bleibt + BOOL bAny = FALSE; + for (i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + { + ((ScDdeLink*)pBase)->ResetValue(); + bAny = TRUE; + } + } + if (bAny) + { + // Formeln berechnen und painten wie im TrackTimeHdl + TrackFormulas(); + pShell->Broadcast( SfxSimpleHint( FID_DATACHANGED ) ); + ResetChanged( ScRange(0,0,0,MAXCOL,MAXROW,MAXTAB) ); + + // wenn FID_DATACHANGED irgendwann mal asynchron werden sollte + // (z.B. mit Invalidate am Window), muss hier ein Update erzwungen werden. + } + + // nun wirklich updaten... + for (i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + ((ScDdeLink*)pBase)->TryUpdate(); // bei DDE-Links TryUpdate statt Update + } + } +} + +BOOL ScDocument::UpdateDdeLink( const String& rAppl, const String& rTopic, const String& rItem ) +{ + // fuer refresh() per StarOne Api + // ResetValue() fuer einzelnen Link nicht noetig + //! wenn's mal alles asynchron wird, aber auch hier + + BOOL bFound = FALSE; + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + { + ScDdeLink* pDdeLink = (ScDdeLink*)pBase; + if ( pDdeLink->GetAppl() == rAppl && + pDdeLink->GetTopic() == rTopic && + pDdeLink->GetItem() == rItem ) + { + pDdeLink->TryUpdate(); + bFound = TRUE; // koennen theoretisch mehrere sein (Mode), darum weitersuchen + } + } + } + } + return bFound; +} + +void ScDocument::DisconnectDdeLinks() +{ + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + pBase->Disconnect(); // bleibt im LinkManager eingetragen + } + } +} + +void ScDocument::CopyDdeLinks( ScDocument* pDestDoc ) const +{ + if (bIsClip) // aus Stream erzeugen + { + if (pClipData) + { + pClipData->Seek(0); + pDestDoc->LoadDdeLinks(*pClipData); + } + } + else if (GetLinkManager()) // Links direkt kopieren + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScDdeLink)) + { + ScDdeLink* pNew = new ScDdeLink( pDestDoc, *(ScDdeLink*)pBase ); + + pDestDoc->pLinkManager->InsertDDELink( pNew, + pNew->GetAppl(), pNew->GetTopic(), pNew->GetItem() ); + } + } + } +} + +USHORT ScDocument::GetDdeLinkCount() const +{ + USHORT nDdeCount = 0; + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + if ((*rLinks[i])->ISA(ScDdeLink)) + ++nDdeCount; + } + return nDdeCount; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Tries to find the specified DDE link. + @param pnDdePos (out-param) if not 0, the index of the DDE link is returned here + (does not include other links from link manager). + @return The DDE link, if it exists, otherwise 0. */ +ScDdeLink* lclGetDdeLink( + const SvxLinkManager* pLinkManager, + const String& rAppl, const String& rTopic, const String& rItem, BYTE nMode, + USHORT* pnDdePos = NULL ) +{ + if( pLinkManager ) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + if( pnDdePos ) *pnDdePos = 0; + for( USHORT nIndex = 0; nIndex < nCount; ++nIndex ) + { + ::sfx2::SvBaseLink* pLink = *rLinks[ nIndex ]; + if( ScDdeLink* pDdeLink = PTR_CAST( ScDdeLink, pLink ) ) + { + if( (pDdeLink->GetAppl() == rAppl) && + (pDdeLink->GetTopic() == rTopic) && + (pDdeLink->GetItem() == rItem) && + ((nMode == SC_DDE_IGNOREMODE) || (nMode == pDdeLink->GetMode())) ) + return pDdeLink; + if( pnDdePos ) ++*pnDdePos; + } + } + } + return NULL; +} + +/** Returns a pointer to the specified DDE link. + @param nDdePos Index of the DDE link (does not include other links from link manager). + @return The DDE link, if it exists, otherwise 0. */ +ScDdeLink* lclGetDdeLink( const SvxLinkManager* pLinkManager, USHORT nDdePos ) +{ + if( pLinkManager ) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + USHORT nDdeIndex = 0; // counts only the DDE links + for( USHORT nIndex = 0; nIndex < nCount; ++nIndex ) + { + ::sfx2::SvBaseLink* pLink = *rLinks[ nIndex ]; + if( ScDdeLink* pDdeLink = PTR_CAST( ScDdeLink, pLink ) ) + { + if( nDdeIndex == nDdePos ) + return pDdeLink; + ++nDdeIndex; + } + } + } + return NULL; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +bool ScDocument::FindDdeLink( const String& rAppl, const String& rTopic, const String& rItem, BYTE nMode, USHORT& rnDdePos ) +{ + return lclGetDdeLink( GetLinkManager(), rAppl, rTopic, rItem, nMode, &rnDdePos ) != NULL; +} + +bool ScDocument::GetDdeLinkData( USHORT nDdePos, String& rAppl, String& rTopic, String& rItem ) const +{ + if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) ) + { + rAppl = pDdeLink->GetAppl(); + rTopic = pDdeLink->GetTopic(); + rItem = pDdeLink->GetItem(); + return true; + } + return false; +} + +bool ScDocument::GetDdeLinkMode( USHORT nDdePos, BYTE& rnMode ) const +{ + if( const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) ) + { + rnMode = pDdeLink->GetMode(); + return true; + } + return false; +} + +const ScMatrix* ScDocument::GetDdeLinkResultMatrix( USHORT nDdePos ) const +{ + const ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ); + return pDdeLink ? pDdeLink->GetResult() : NULL; +} + +bool ScDocument::CreateDdeLink( const String& rAppl, const String& rTopic, const String& rItem, BYTE nMode, ScMatrix* pResults ) +{ + /* Create a DDE link without updating it (i.e. for Excel import), to prevent + unwanted connections. First try to find existing link. Set result array + on existing and new links. */ + //! store DDE links additionally at document (for efficiency)? + DBG_ASSERT( nMode != SC_DDE_IGNOREMODE, "ScDocument::CreateDdeLink - SC_DDE_IGNOREMODE not allowed here" ); + if( GetLinkManager() && (nMode != SC_DDE_IGNOREMODE) ) + { + ScDdeLink* pDdeLink = lclGetDdeLink( pLinkManager, rAppl, rTopic, rItem, nMode ); + if( !pDdeLink ) + { + // create a new DDE link, but without TryUpdate + pDdeLink = new ScDdeLink( this, rAppl, rTopic, rItem, nMode ); + pLinkManager->InsertDDELink( pDdeLink, rAppl, rTopic, rItem ); + } + + // insert link results + if( pResults ) + pDdeLink->SetResult( pResults ); + + return true; + } + return false; +} + +bool ScDocument::SetDdeLinkResultMatrix( USHORT nDdePos, ScMatrix* pResults ) +{ + if( ScDdeLink* pDdeLink = lclGetDdeLink( GetLinkManager(), nDdePos ) ) + { + pDdeLink->SetResult( pResults ); + return true; + } + return false; +} + +//------------------------------------------------------------------------ + +BOOL ScDocument::HasAreaLinks() const +{ + if (GetLinkManager()) // Clipboard z.B. hat keinen LinkManager + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + if ((*rLinks[i])->ISA(ScAreaLink)) + return TRUE; + } + + return FALSE; +} + +void ScDocument::UpdateAreaLinks() +{ + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScAreaLink)) + pBase->Update(); + } + } +} + +void ScDocument::DeleteAreaLinksOnTab( SCTAB nTab ) +{ + if (GetLinkManager()) + { + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nPos = 0; + while ( nPos < rLinks.Count() ) + { + const ::sfx2::SvBaseLink* pBase = *rLinks[nPos]; + if ( pBase->ISA(ScAreaLink) && + static_cast<const ScAreaLink*>(pBase)->GetDestArea().aStart.Tab() == nTab ) + pLinkManager->Remove( nPos ); + else + ++nPos; + } + } +} + +void ScDocument::UpdateRefAreaLinks( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if (GetLinkManager()) + { + bool bAnyUpdate = false; + + const ::sfx2::SvBaseLinks& rLinks = pLinkManager->GetLinks(); + USHORT nCount = rLinks.Count(); + for (USHORT i=0; i<nCount; i++) + { + ::sfx2::SvBaseLink* pBase = *rLinks[i]; + if (pBase->ISA(ScAreaLink)) + { + ScAreaLink* pLink = (ScAreaLink*) pBase; + ScRange aOutRange = pLink->GetDestArea(); + + SCCOL nCol1 = aOutRange.aStart.Col(); + SCROW nRow1 = aOutRange.aStart.Row(); + SCTAB nTab1 = aOutRange.aStart.Tab(); + SCCOL nCol2 = aOutRange.aEnd.Col(); + SCROW nRow2 = aOutRange.aEnd.Row(); + SCTAB nTab2 = aOutRange.aEnd.Tab(); + + ScRefUpdateRes eRes = + ScRefUpdate::Update( this, eUpdateRefMode, + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( eRes != UR_NOTHING ) + { + pLink->SetDestArea( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) ); + bAnyUpdate = true; + } + } + } + + if ( bAnyUpdate ) + { + // #i52120# Look for duplicates (after updating all positions). + // If several links start at the same cell, the one with the lower index is removed + // (file format specifies only one link definition for a cell). + + USHORT nFirstIndex = 0; + while ( nFirstIndex < nCount ) + { + bool bFound = false; + ::sfx2::SvBaseLink* pFirst = *rLinks[nFirstIndex]; + if ( pFirst->ISA(ScAreaLink) ) + { + ScAddress aFirstPos = static_cast<ScAreaLink*>(pFirst)->GetDestArea().aStart; + for ( USHORT nSecondIndex = nFirstIndex + 1; nSecondIndex < nCount && !bFound; ++nSecondIndex ) + { + ::sfx2::SvBaseLink* pSecond = *rLinks[nSecondIndex]; + if ( pSecond->ISA(ScAreaLink) && + static_cast<ScAreaLink*>(pSecond)->GetDestArea().aStart == aFirstPos ) + { + // remove the first link, exit the inner loop, don't increment nFirstIndex + pLinkManager->Remove( pFirst ); + nCount = rLinks.Count(); + bFound = true; + } + } + } + if (!bFound) + ++nFirstIndex; + } + } + } +} + +//------------------------------------------------------------------------ + +// TimerDelays etc. +void ScDocument::KeyInput( const KeyEvent& ) +{ + if ( pChartListenerCollection->GetCount() ) + pChartListenerCollection->StartTimer(); + if( apTemporaryChartLock.get() ) + apTemporaryChartLock->StartOrContinueLocking(); +} + +// ---------------------------------------------------------------------------- + +BOOL ScDocument::CheckMacroWarn() +{ + // The check for macro configuration, macro warning and disabling is now handled + // in SfxObjectShell::AdjustMacroMode, called by SfxObjectShell::CallBasic. + + return TRUE; +} + +//------------------------------------------------------------------------ + +SfxBindings* ScDocument::GetViewBindings() +{ + // used to invalidate slots after changes to this document + + if ( !pShell ) + return NULL; // no ObjShell -> no view + + // first check current view + SfxViewFrame* pViewFrame = SfxViewFrame::Current(); + if ( pViewFrame && pViewFrame->GetObjectShell() != pShell ) // wrong document? + pViewFrame = NULL; + + // otherwise use first view for this doc + if ( !pViewFrame ) + pViewFrame = SfxViewFrame::GetFirst( pShell ); + + if (pViewFrame) + return &pViewFrame->GetBindings(); + else + return NULL; +} + +//------------------------------------------------------------------------ + +void lcl_TransliterateEditEngine( ScEditEngineDefaulter& rEngine, + utl::TransliterationWrapper& rTranslitarationWrapper, + BOOL bConsiderLanguage, ScDocument* pDoc ) +{ + //! should use TransliterateText method of EditEngine instead, when available! + + sal_uInt16 nLanguage = LANGUAGE_SYSTEM; + + USHORT nParCount = rEngine.GetParagraphCount(); + for (USHORT nPar=0; nPar<nParCount; nPar++) + { + SvUShorts aPortions; + rEngine.GetPortions( (USHORT)nPar, aPortions ); + + for ( USHORT nPos = aPortions.Count(); nPos; ) + { + --nPos; + USHORT nEnd = aPortions.GetObject( nPos ); + USHORT nStart = nPos ? aPortions.GetObject( nPos - 1 ) : 0; + + ESelection aSel( nPar, nStart, nPar, nEnd ); + String aOldStr = rEngine.GetText( aSel ); + SfxItemSet aAttr = rEngine.GetAttribs( aSel ); + + if ( aAttr.GetItemState( EE_FEATURE_FIELD ) != SFX_ITEM_ON ) // fields are not touched + { + if ( bConsiderLanguage ) + { + BYTE nScript = pDoc->GetStringScriptType( aOldStr ); + USHORT nWhich = ( nScript == SCRIPTTYPE_ASIAN ) ? EE_CHAR_LANGUAGE_CJK : + ( ( nScript == SCRIPTTYPE_COMPLEX ) ? EE_CHAR_LANGUAGE_CTL : + EE_CHAR_LANGUAGE ); + nLanguage = ((const SvxLanguageItem&)aAttr.Get(nWhich)).GetValue(); + } + + com::sun::star::uno::Sequence<sal_Int32> aOffsets; + String aNewStr = rTranslitarationWrapper.transliterate( aOldStr, nLanguage, 0, aOldStr.Len(), &aOffsets ); + + if ( aNewStr != aOldStr ) + { + // replace string, keep attributes + + rEngine.QuickInsertText( aNewStr, aSel ); + aSel.nEndPos = aSel.nStartPos + aNewStr.Len(); + rEngine.QuickSetAttribs( aAttr, aSel ); + } + } + } + } +} + +void ScDocument::TransliterateText( const ScMarkData& rMultiMark, sal_Int32 nType ) +{ + DBG_ASSERT( rMultiMark.IsMultiMarked(), "TransliterateText: no selection" ); + + utl::TransliterationWrapper aTranslitarationWrapper( xServiceManager, nType ); + BOOL bConsiderLanguage = aTranslitarationWrapper.needLanguageForTheMode(); + sal_uInt16 nLanguage = LANGUAGE_SYSTEM; + + ScEditEngineDefaulter* pEngine = NULL; // not using pEditEngine member because of defaults + + SCTAB nCount = GetTableCount(); + for (SCTAB nTab = 0; nTab < nCount; nTab++) + if ( pTab[nTab] && rMultiMark.GetTableSelect(nTab) ) + { + SCCOL nCol = 0; + SCROW nRow = 0; + + BOOL bFound = rMultiMark.IsCellMarked( nCol, nRow ); + if (!bFound) + bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark ); + + while (bFound) + { + const ScBaseCell* pCell = GetCell( ScAddress( nCol, nRow, nTab ) ); + CellType eType = pCell ? pCell->GetCellType() : CELLTYPE_NONE; + + if ( eType == CELLTYPE_STRING ) + { + String aOldStr; + ((const ScStringCell*)pCell)->GetString(aOldStr); + xub_StrLen nOldLen = aOldStr.Len(); + + if ( bConsiderLanguage ) + { + BYTE nScript = GetStringScriptType( aOldStr ); //! cell script type? + USHORT nWhich = ( nScript == SCRIPTTYPE_ASIAN ) ? ATTR_CJK_FONT_LANGUAGE : + ( ( nScript == SCRIPTTYPE_COMPLEX ) ? ATTR_CTL_FONT_LANGUAGE : + ATTR_FONT_LANGUAGE ); + nLanguage = ((const SvxLanguageItem*)GetAttr( nCol, nRow, nTab, nWhich ))->GetValue(); + } + + com::sun::star::uno::Sequence<sal_Int32> aOffsets; + String aNewStr = aTranslitarationWrapper.transliterate( aOldStr, nLanguage, 0, nOldLen, &aOffsets ); + + if ( aNewStr != aOldStr ) + PutCell( nCol, nRow, nTab, new ScStringCell( aNewStr ) ); + } + else if ( eType == CELLTYPE_EDIT ) + { + if (!pEngine) + pEngine = new ScFieldEditEngine( GetEnginePool(), GetEditPool() ); + + // defaults from cell attributes must be set so right language is used + const ScPatternAttr* pPattern = GetPattern( nCol, nRow, nTab ); + SfxItemSet* pDefaults = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pPattern->FillEditItemSet( pDefaults ); + pEngine->SetDefaults( pDefaults, TRUE ); + + const EditTextObject* pData = ((const ScEditCell*)pCell)->GetData(); + pEngine->SetText( *pData ); + + pEngine->ClearModifyFlag(); + + lcl_TransliterateEditEngine( *pEngine, aTranslitarationWrapper, bConsiderLanguage, this ); + + if ( pEngine->IsModified() ) + { + ScEditAttrTester aTester( pEngine ); + if ( aTester.NeedsObject() ) + { + // remove defaults (paragraph attributes) before creating text object + SfxItemSet* pEmpty = new SfxItemSet( pEngine->GetEmptyItemSet() ); + pEngine->SetDefaults( pEmpty, TRUE ); + + EditTextObject* pNewData = pEngine->CreateTextObject(); + PutCell( nCol, nRow, nTab, + new ScEditCell( pNewData, this, pEngine->GetEditTextObjectPool() ) ); + delete pNewData; + } + else + { + String aNewStr = pEngine->GetText(); + PutCell( nCol, nRow, nTab, new ScStringCell( aNewStr ) ); + } + } + } + + bFound = GetNextMarkedCell( nCol, nRow, nTab, rMultiMark ); + } + } + + delete pEngine; +} + diff --git a/sc/source/core/data/documen9.cxx b/sc/source/core/data/documen9.cxx new file mode 100644 index 000000000000..de27a80b17ee --- /dev/null +++ b/sc/source/core/data/documen9.cxx @@ -0,0 +1,883 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: documen9.cxx,v $ + * $Revision: 1.43.52.5 $ + * + * 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 <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/chart2/data/XDataReceiver.hpp> + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <sot/exchange.hxx> +#include <svx/akrnitem.hxx> +#include <svx/fontitem.hxx> +#include <svx/forbiddencharacterstable.hxx> +#include <svx/langitem.hxx> +#include <svx/svdetc.hxx> +#include <svx/svditer.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdouno.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdundo.hxx> +#include <svx/xtable.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/printer.hxx> +#include <svtools/saveopt.hxx> +#include <svtools/pathoptions.hxx> + +#include "document.hxx" +#include "docoptio.hxx" +#include "table.hxx" +#include "drwlayer.hxx" +#include "markdata.hxx" +#include "patattr.hxx" +#include "rechead.hxx" +#include "poolhelp.hxx" +#include "docpool.hxx" +#include "chartarr.hxx" +#include "detfunc.hxx" // for UpdateAllComments +#include "editutil.hxx" +#include "postit.hxx" + +using namespace ::com::sun::star; + +// ----------------------------------------------------------------------- + + +SfxBroadcaster* ScDocument::GetDrawBroadcaster() +{ + return pDrawLayer; +} + +void ScDocument::BeginDrawUndo() +{ + if (pDrawLayer) + pDrawLayer->BeginCalcUndo(); +} + +XColorTable* ScDocument::GetColorTable() +{ + if (pDrawLayer) + return pDrawLayer->GetColorTable(); + else + { + if (!pColorTable) + { + SvtPathOptions aPathOpt; + pColorTable = new XColorTable( aPathOpt.GetPalettePath() ); + } + + return pColorTable; + } +} + +BOOL lcl_AdjustRanges( ScRangeList& rRanges, SCTAB nSource, SCTAB nDest, SCTAB nTabCount ) +{ + //! if multiple sheets are copied, update references into the other copied sheets? + + BOOL bChanged = FALSE; + + ULONG nCount = rRanges.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange* pRange = rRanges.GetObject(i); + if ( pRange->aStart.Tab() == nSource && pRange->aEnd.Tab() == nSource ) + { + pRange->aStart.SetTab( nDest ); + pRange->aEnd.SetTab( nDest ); + bChanged = TRUE; + } + if ( pRange->aStart.Tab() >= nTabCount ) + { + pRange->aStart.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 ); + bChanged = TRUE; + } + if ( pRange->aEnd.Tab() >= nTabCount ) + { + pRange->aEnd.SetTab( nTabCount > 0 ? ( nTabCount - 1 ) : 0 ); + bChanged = TRUE; + } + } + + return bChanged; +} + +void ScDocument::TransferDrawPage(ScDocument* pSrcDoc, SCTAB nSrcPos, SCTAB nDestPos) +{ + if (pDrawLayer && pSrcDoc->pDrawLayer) + { + SdrPage* pOldPage = pSrcDoc->pDrawLayer->GetPage(static_cast<sal_uInt16>(nSrcPos)); + SdrPage* pNewPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nDestPos)); + + if (pOldPage && pNewPage) + { + SdrObjListIter aIter( *pOldPage, IM_FLAT ); + SdrObject* pOldObject = aIter.Next(); + while (pOldObject) + { + // #116235# + SdrObject* pNewObject = pOldObject->Clone(); + // SdrObject* pNewObject = pOldObject->Clone( pNewPage, pDrawLayer ); + pNewObject->SetModel(pDrawLayer); + pNewObject->SetPage(pNewPage); + + pNewObject->NbcMove(Size(0,0)); + pNewPage->InsertObject( pNewObject ); + + if (pDrawLayer->IsRecording()) + pDrawLayer->AddCalcUndo( new SdrUndoInsertObj( *pNewObject ) ); + + // #71726# if it's a chart, make sure the data references are valid + // (this must be after InsertObject!) + + if ( pNewObject->GetObjIdentifier() == OBJ_OLE2 ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pNewObject)->GetObjRef(); + uno::Reference< embed::XClassifiedObject > xClassified( xIPObj, uno::UNO_QUERY ); + SvGlobalName aObjectClassName; + if ( xClassified.is() ) + { + try { + aObjectClassName = SvGlobalName( xClassified->getClassID() ); + } catch( uno::Exception& ) + { + // TODO: handle error? + } + } + + if ( xIPObj.is() && SotExchange::IsChart( aObjectClassName ) ) + { + String aChartName = ((SdrOle2Obj*)pNewObject)->GetPersistName(); + + uno::Reference< chart2::XChartDocument > xChartDoc( GetChartByName( aChartName ) ); + uno::Reference< chart2::data::XDataReceiver > xReceiver( xChartDoc, uno::UNO_QUERY ); + if( xChartDoc.is() && xReceiver.is() ) + { + if( !xChartDoc->hasInternalDataProvider() ) + { + ::std::vector< ScRangeList > aRangesVector; + GetChartRanges( aChartName, aRangesVector, pSrcDoc ); + + ::std::vector< ScRangeList >::iterator aIt( aRangesVector.begin() ); + for( ; aIt!=aRangesVector.end(); aIt++ ) + { + ScRangeList& rScRangeList( *aIt ); + lcl_AdjustRanges( rScRangeList, nSrcPos, nDestPos, GetTableCount() ); + } + SetChartRanges( aChartName, aRangesVector ); + } + } + } + } + + pOldObject = aIter.Next(); + } + } + } +} + +void ScDocument::InitDrawLayer( SfxObjectShell* pDocShell ) +{ + if (pDocShell && !pShell) + pShell = pDocShell; + +// DBG_ASSERT(pShell,"InitDrawLayer ohne Shell"); + + if (!pDrawLayer) + { + String aName; + if ( pShell && !pShell->IsLoading() ) // #88438# don't call GetTitle while loading + aName = pShell->GetTitle(); + pDrawLayer = new ScDrawLayer( this, aName ); + if (GetLinkManager()) + pDrawLayer->SetLinkManager( pLinkManager ); + + // Drawing pages are accessed by table number, so they must also be present + // for preceding table numbers, even if the tables aren't allocated + // (important for clipboard documents). + + SCTAB nDrawPages = 0; + SCTAB nTab; + for (nTab=0; nTab<=MAXTAB; nTab++) + if (pTab[nTab]) + nDrawPages = nTab + 1; // needed number of pages + + for (nTab=0; nTab<nDrawPages; nTab++) + { + pDrawLayer->ScAddPage( nTab ); // always add page, with or without the table + if (pTab[nTab]) + { + String aTabName; + pTab[nTab]->GetName(aTabName); + pDrawLayer->ScRenamePage( nTab, aTabName ); + + pTab[nTab]->SetDrawPageSize(false); // #54782# set the right size immediately +#if 0 + ULONG nx = (ULONG) ((double) (MAXCOL+1) * STD_COL_WIDTH * HMM_PER_TWIPS ); + ULONG ny = (ULONG) ((double) (MAXROW+1) * ScGlobal::nStdRowHeight * HMM_PER_TWIPS ); + pDrawLayer->SetPageSize( nTab, Size( nx, ny ) ); +#endif + } + } + + pDrawLayer->SetDefaultTabulator( GetDocOptions().GetTabDistance() ); + + UpdateDrawPrinter(); + UpdateDrawDefaults(); + UpdateDrawLanguages(); + if (bImportingXML) + pDrawLayer->EnableAdjust(FALSE); + + pDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters ); + pDrawLayer->SetCharCompressType( GetAsianCompression() ); + pDrawLayer->SetKernAsianPunctuation( GetAsianKerning() ); + } +} + +void ScDocument::UpdateDrawLanguages() +{ + if (pDrawLayer) + { + SfxItemPool& rDrawPool = pDrawLayer->GetItemPool(); + rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eLanguage, EE_CHAR_LANGUAGE ) ); + rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eCjkLanguage, EE_CHAR_LANGUAGE_CJK ) ); + rDrawPool.SetPoolDefaultItem( SvxLanguageItem( eCtlLanguage, EE_CHAR_LANGUAGE_CTL ) ); + } +} + +void ScDocument::UpdateDrawDefaults() +{ + // drawing layer defaults that are set for new documents (if InitNew was called) + + if ( pDrawLayer && bSetDrawDefaults ) + { + SfxItemPool& rDrawPool = pDrawLayer->GetItemPool(); + rDrawPool.SetPoolDefaultItem( SvxAutoKernItem( TRUE, EE_CHAR_PAIRKERNING ) ); + } +} + +void ScDocument::UpdateDrawPrinter() +{ + if (pDrawLayer) + { + // use the printer even if IsValid is false + // Application::GetDefaultDevice causes trouble with changing MapModes + +// OutputDevice* pRefDev = GetPrinter(); +// pRefDev->SetMapMode( MAP_100TH_MM ); + pDrawLayer->SetRefDevice(GetRefDevice()); + } +} + +sal_Bool ScDocument::IsChart( const SdrObject* pObject ) +{ + // #109985# + // IsChart() implementation moved to svx drawinglayer + if(pObject && OBJ_OLE2 == pObject->GetObjIdentifier()) + { + return ((SdrOle2Obj*)pObject)->IsChart(); + } + + return sal_False; +} + +IMPL_LINK_INLINE_START( ScDocument, GetUserDefinedColor, USHORT *, pColorIndex ) +{ + return (long) &((GetColorTable()->GetColor(*pColorIndex))->GetColor()); +} +IMPL_LINK_INLINE_END( ScDocument, GetUserDefinedColor, USHORT *, pColorIndex ) + +void ScDocument::DeleteDrawLayer() +{ + delete pDrawLayer; +} + +void ScDocument::DeleteColorTable() +{ + delete pColorTable; +} + +BOOL ScDocument::DrawGetPrintArea( ScRange& rRange, BOOL bSetHor, BOOL bSetVer ) const +{ + return pDrawLayer->GetPrintArea( rRange, bSetHor, bSetVer ); +} + +void ScDocument::DrawMovePage( USHORT nOldPos, USHORT nNewPos ) +{ + pDrawLayer->ScMovePage(nOldPos,nNewPos); +} + +void ScDocument::DrawCopyPage( USHORT nOldPos, USHORT nNewPos ) +{ + // angelegt wird die Page schon im ScTable ctor + pDrawLayer->ScCopyPage( nOldPos, nNewPos, FALSE ); +} + +void ScDocument::DeleteObjectsInArea( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark ) +{ + if (!pDrawLayer) + return; + + SCTAB nTabCount = GetTableCount(); + for (SCTAB nTab=0; nTab<=nTabCount; nTab++) + if (pTab[nTab] && rMark.GetTableSelect(nTab)) + pDrawLayer->DeleteObjectsInArea( nTab, nCol1, nRow1, nCol2, nRow2 ); +} + +void ScDocument::DeleteObjectsInSelection( const ScMarkData& rMark ) +{ + if (!pDrawLayer) + return; + + pDrawLayer->DeleteObjectsInSelection( rMark ); +} + +BOOL ScDocument::HasOLEObjectsInArea( const ScRange& rRange, const ScMarkData* pTabMark ) +{ + // pTabMark is used only for selected tables. If pTabMark is 0, all tables of rRange are used. + + if (!pDrawLayer) + return FALSE; + + SCTAB nStartTab = 0; + SCTAB nEndTab = MAXTAB; + if ( !pTabMark ) + { + nStartTab = rRange.aStart.Tab(); + nEndTab = rRange.aEnd.Tab(); + } + + for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++) + { + if ( !pTabMark || pTabMark->GetTableSelect(nTab) ) + { + Rectangle aMMRect = GetMMRect( rRange.aStart.Col(), rRange.aStart.Row(), + rRange.aEnd.Col(), rRange.aEnd.Row(), nTab ); + + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_OLE2 && + aMMRect.IsInside( pObject->GetCurrentBoundRect() ) ) + return TRUE; + + pObject = aIter.Next(); + } + } + } + } + + return FALSE; +} + + +void ScDocument::StartAnimations( SCTAB nTab, Window* pWin ) +{ + if (!pDrawLayer) + return; + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) + return; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if (pObject->ISA(SdrGrafObj)) + { + SdrGrafObj* pGrafObj = (SdrGrafObj*)pObject; + if ( pGrafObj->IsAnimated() ) + { + const Rectangle& rRect = pGrafObj->GetCurrentBoundRect(); + pGrafObj->StartAnimation( pWin, rRect.TopLeft(), rRect.GetSize() ); + } + } + pObject = aIter.Next(); + } +} + +//UNUSED2008-05 void ScDocument::RefreshNoteFlags() +//UNUSED2008-05 { +//UNUSED2008-05 if (!pDrawLayer) +//UNUSED2008-05 return; +//UNUSED2008-05 +//UNUSED2008-05 BOOL bAnyIntObj = FALSE; +//UNUSED2008-05 SCTAB nTab; +//UNUSED2008-05 ScPostIt aNote(this); +//UNUSED2008-05 for (nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++) +//UNUSED2008-05 { +//UNUSED2008-05 SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); +//UNUSED2008-05 DBG_ASSERT(pPage,"Page ?"); +//UNUSED2008-05 if (pPage) +//UNUSED2008-05 { +//UNUSED2008-05 SdrObjListIter aIter( *pPage, IM_FLAT ); +//UNUSED2008-05 SdrObject* pObject = aIter.Next(); +//UNUSED2008-05 while (pObject) +//UNUSED2008-05 { +//UNUSED2008-05 if ( pObject->GetLayer() == SC_LAYER_INTERN ) +//UNUSED2008-05 { +//UNUSED2008-05 bAnyIntObj = TRUE; // for all internal objects, including detective +//UNUSED2008-05 +//UNUSED2008-05 if ( pObject->ISA( SdrCaptionObj ) ) +//UNUSED2008-05 { +//UNUSED2008-05 ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject ); +//UNUSED2008-05 if ( pData ) +//UNUSED2008-05 { +//UNUSED2008-05 if ( GetNote( pData->aStt.Col(), pData->aStt.Row(), nTab, aNote)) +//UNUSED2008-05 if ( !aNote.IsShown() ) +//UNUSED2008-05 { +//UNUSED2008-05 aNote.SetShown(TRUE); +//UNUSED2008-05 SetNote( pData->aStt.Col(), pData->aStt.Row(), nTab, aNote); +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 pObject = aIter.Next(); +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 if (bAnyIntObj) +//UNUSED2008-05 { +//UNUSED2008-05 // update attributes for all note objects and the colors of detective objects +//UNUSED2008-05 // (we don't know with which settings the file was created) +//UNUSED2008-05 +//UNUSED2008-05 ScDetectiveFunc aFunc( this, 0 ); +//UNUSED2008-05 aFunc.UpdateAllComments(); +//UNUSED2008-05 aFunc.UpdateAllArrowColors(); +//UNUSED2008-05 } +//UNUSED2008-05 } + +BOOL ScDocument::HasBackgroundDraw( SCTAB nTab, const Rectangle& rMMRect ) +{ + // Gibt es Objekte auf dem Hintergrund-Layer, die (teilweise) von rMMRect + // betroffen sind? + // (fuer Drawing-Optimierung, vor dem Hintergrund braucht dann nicht geloescht + // zu werden) + + if (!pDrawLayer) + return FALSE; + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) + return FALSE; + + BOOL bFound = FALSE; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + if ( pObject->GetLayer() == SC_LAYER_BACK && pObject->GetCurrentBoundRect().IsOver( rMMRect ) ) + bFound = TRUE; + pObject = aIter.Next(); + } + + return bFound; +} + +BOOL ScDocument::HasAnyDraw( SCTAB nTab, const Rectangle& rMMRect ) +{ + // Gibt es ueberhaupt Objekte, die (teilweise) von rMMRect + // betroffen sind? + // (um leere Seiten beim Drucken zu erkennen) + + if (!pDrawLayer) + return FALSE; + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) + return FALSE; + + BOOL bFound = FALSE; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + if ( pObject->GetCurrentBoundRect().IsOver( rMMRect ) ) + bFound = TRUE; + pObject = aIter.Next(); + } + + return bFound; +} + +void ScDocument::EnsureGraphicNames() +{ + if (pDrawLayer) + pDrawLayer->EnsureGraphicNames(); +} + +SdrObject* ScDocument::GetObjectAtPoint( SCTAB nTab, const Point& rPos ) +{ + // fuer Drag&Drop auf Zeichenobjekt + + SdrObject* pFound = NULL; + if (pDrawLayer && pTab[nTab]) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( pObject->GetCurrentBoundRect().IsInside(rPos) ) + { + // Intern interessiert gar nicht + // Objekt vom Back-Layer nur, wenn kein Objekt von anderem Layer getroffen + + SdrLayerID nLayer = pObject->GetLayer(); + if ( (nLayer != SC_LAYER_INTERN) && (nLayer != SC_LAYER_HIDDEN) ) + { + if ( nLayer != SC_LAYER_BACK || + !pFound || pFound->GetLayer() == SC_LAYER_BACK ) + { + pFound = pObject; + } + } + } + // weitersuchen -> letztes (oberstes) getroffenes Objekt nehmen + + pObject = aIter.Next(); + } + } + } + return pFound; +} + +BOOL ScDocument::IsPrintEmpty( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, BOOL bLeftIsEmpty, + ScRange* pLastRange, Rectangle* pLastMM ) const +{ + if (!IsBlockEmpty( nTab, nStartCol, nStartRow, nEndCol, nEndRow )) + return FALSE; + + ScDocument* pThis = (ScDocument*)this; //! GetMMRect / HasAnyDraw etc. const !!! + + Rectangle aMMRect; + if ( pLastRange && pLastMM && nTab == pLastRange->aStart.Tab() && + nStartRow == pLastRange->aStart.Row() && nEndRow == pLastRange->aEnd.Row() ) + { + // keep vertical part of aMMRect, only update horizontal position + aMMRect = *pLastMM; + + long nLeft = 0; + SCCOL i; + for (i=0; i<nStartCol; i++) + nLeft += GetColWidth(i,nTab); + long nRight = nLeft; + for (i=nStartCol; i<=nEndCol; i++) + nRight += GetColWidth(i,nTab); + + aMMRect.Left() = (long)(nLeft * HMM_PER_TWIPS); + aMMRect.Right() = (long)(nRight * HMM_PER_TWIPS); + } + else + aMMRect = pThis->GetMMRect( nStartCol, nStartRow, nEndCol, nEndRow, nTab ); + + if ( pLastRange && pLastMM ) + { + *pLastRange = ScRange( nStartCol, nStartRow, nTab, nEndCol, nEndRow, nTab ); + *pLastMM = aMMRect; + } + + if ( pThis->HasAnyDraw( nTab, aMMRect )) + return FALSE; + + if ( nStartCol > 0 && !bLeftIsEmpty ) + { + // aehnlich wie in ScPrintFunc::AdjustPrintArea + //! ExtendPrintArea erst ab Start-Spalte des Druckbereichs + + SCCOL nExtendCol = nStartCol - 1; + SCROW nTmpRow = nEndRow; + + pThis->ExtendMerge( 0,nStartRow, nExtendCol,nTmpRow, nTab, + FALSE, TRUE ); // kein Refresh, incl. Attrs + + OutputDevice* pDev = pThis->GetPrinter(); + pDev->SetMapMode( MAP_PIXEL ); // wichtig fuer GetNeededSize + pThis->ExtendPrintArea( pDev, nTab, 0, nStartRow, nExtendCol, nEndRow ); + if ( nExtendCol >= nStartCol ) + return FALSE; + } + + return TRUE; +} + +void ScDocument::Clear( sal_Bool bFromDestructor ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) + { + delete pTab[i]; + pTab[i]=NULL; + } + delete pSelectionAttr; + pSelectionAttr = NULL; + + if (pDrawLayer) + { + // #116168# + //pDrawLayer->Clear(); + pDrawLayer->ClearModel( bFromDestructor ); + } +} + +BOOL ScDocument::HasControl( SCTAB nTab, const Rectangle& rMMRect ) +{ + BOOL bFound = FALSE; + + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + if (pObject->ISA(SdrUnoObj)) + { + Rectangle aObjRect = pObject->GetLogicRect(); + if ( aObjRect.IsOver( rMMRect ) ) + bFound = TRUE; + } + + pObject = aIter.Next(); + } + } + } + + return bFound; +} + +void ScDocument::InvalidateControls( Window* pWin, SCTAB nTab, const Rectangle& rMMRect ) +{ + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if (pObject->ISA(SdrUnoObj)) + { + Rectangle aObjRect = pObject->GetLogicRect(); + if ( aObjRect.IsOver( rMMRect ) ) + { + // Uno-Controls zeichnen sich immer komplett, ohne Ruecksicht + // auf ClippingRegions. Darum muss das ganze Objekt neu gepainted + // werden, damit die Selektion auf der Tabelle nicht uebermalt wird. + + //pWin->Invalidate( aObjRect.GetIntersection( rMMRect ) ); + pWin->Invalidate( aObjRect ); + } + } + + pObject = aIter.Next(); + } + } + } +} + +BOOL ScDocument::HasDetectiveObjects(SCTAB nTab) const +{ + // looks for detective objects, annotations don't count + // (used to adjust scale so detective objects hit their cells better) + + BOOL bFound = FALSE; + + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject && !bFound) + { + // anything on the internal layer except captions (annotations) + if ( (pObject->GetLayer() == SC_LAYER_INTERN) && !ScDrawLayer::IsNoteCaption( pObject ) ) + bFound = TRUE; + + pObject = aIter.Next(); + } + } + } + + return bFound; +} + +void ScDocument::UpdateFontCharSet() +{ + // In alten Versionen (bis incl. 4.0 ohne SP) wurden beim Austausch zwischen + // Systemen die CharSets in den Font-Attributen nicht angepasst. + // Das muss fuer Dokumente bis incl SP2 nun nachgeholt werden: + // Alles, was nicht SYMBOL ist, wird auf den System-CharSet umgesetzt. + // Bei neuen Dokumenten (Version SC_FONTCHARSET) sollte der CharSet stimmen. + + BOOL bUpdateOld = ( nSrcVer < SC_FONTCHARSET ); + + CharSet eSysSet = gsl_getSystemTextEncoding(); + if ( eSrcSet != eSysSet || bUpdateOld ) + { + USHORT nCount,i; + SvxFontItem* pItem; + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + nCount = pPool->GetItemCount(ATTR_FONT); + for (i=0; i<nCount; i++) + { + pItem = (SvxFontItem*)pPool->GetItem(ATTR_FONT, i); + if ( pItem && ( pItem->GetCharSet() == eSrcSet || + ( bUpdateOld && pItem->GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) ) + pItem->GetCharSet() = eSysSet; + } + + if ( pDrawLayer ) + { + SfxItemPool& rDrawPool = pDrawLayer->GetItemPool(); + nCount = rDrawPool.GetItemCount(EE_CHAR_FONTINFO); + for (i=0; i<nCount; i++) + { + pItem = (SvxFontItem*)rDrawPool.GetItem(EE_CHAR_FONTINFO, i); + if ( pItem && ( pItem->GetCharSet() == eSrcSet || + ( bUpdateOld && pItem->GetCharSet() != RTL_TEXTENCODING_SYMBOL ) ) ) + pItem->GetCharSet() = eSysSet; + } + } + } +} + +void ScDocument::SetImportingXML( BOOL bVal ) +{ + bImportingXML = bVal; + if (pDrawLayer) + pDrawLayer->EnableAdjust(!bImportingXML); + + if ( !bVal ) + { + // #i57869# after loading, do the real RTL mirroring for the sheets that have the LoadingRTL flag set + + for ( SCTAB nTab=0; nTab<=MAXTAB && pTab[nTab]; nTab++ ) + if ( pTab[nTab]->IsLoadingRTL() ) + { + pTab[nTab]->SetLoadingRTL( FALSE ); + SetLayoutRTL( nTab, TRUE ); // includes mirroring; bImportingXML must be cleared first + } + } +} + +void ScDocument::SetXMLFromWrapper( BOOL bVal ) +{ + bXMLFromWrapper = bVal; +} + +vos::ORef<SvxForbiddenCharactersTable> ScDocument::GetForbiddenCharacters() +{ + return xForbiddenCharacters; +} + +void ScDocument::SetForbiddenCharacters( const vos::ORef<SvxForbiddenCharactersTable> xNew ) +{ + xForbiddenCharacters = xNew; + if ( pEditEngine ) + pEditEngine->SetForbiddenCharsTable( xForbiddenCharacters ); + if ( pDrawLayer ) + pDrawLayer->SetForbiddenCharsTable( xForbiddenCharacters ); +} + +BOOL ScDocument::IsValidAsianCompression() const +{ + return ( nAsianCompression != SC_ASIANCOMPRESSION_INVALID ); +} + +BYTE ScDocument::GetAsianCompression() const +{ + if ( nAsianCompression == SC_ASIANCOMPRESSION_INVALID ) + return 0; + else + return nAsianCompression; +} + +void ScDocument::SetAsianCompression(BYTE nNew) +{ + nAsianCompression = nNew; + if ( pEditEngine ) + pEditEngine->SetAsianCompressionMode( nAsianCompression ); + if ( pDrawLayer ) + pDrawLayer->SetCharCompressType( nAsianCompression ); +} + +BOOL ScDocument::IsValidAsianKerning() const +{ + return ( nAsianKerning != SC_ASIANKERNING_INVALID ); +} + +BOOL ScDocument::GetAsianKerning() const +{ + if ( nAsianKerning == SC_ASIANKERNING_INVALID ) + return FALSE; + else + return (BOOL)nAsianKerning; +} + +void ScDocument::SetAsianKerning(BOOL bNew) +{ + nAsianKerning = (BYTE)bNew; + if ( pEditEngine ) + pEditEngine->SetKernAsianPunctuation( (BOOL)nAsianKerning ); + if ( pDrawLayer ) + pDrawLayer->SetKernAsianPunctuation( (BOOL)nAsianKerning ); +} + diff --git a/sc/source/core/data/document.cxx b/sc/source/core/data/document.cxx new file mode 100644 index 000000000000..077983b4382b --- /dev/null +++ b/sc/source/core/data/document.cxx @@ -0,0 +1,4903 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: document.cxx,v $ + * $Revision: 1.90.36.8 $ + * + * 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 --------------------------------------------------------------- + +#define _ZFORLIST_DECLARE_TABLE +#include "scitems.hxx" +#include <svx/eeitem.hxx> + +#include <svx/boxitem.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/pageitem.hxx> +#include <svx/editeng.hxx> +#include <svx/svditer.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdocapt.hxx> +#include <sfx2/app.hxx> +#include <sfx2/objsh.hxx> +#include <svtools/poolcach.hxx> +#include <svtools/saveopt.hxx> +#include <svtools/zforlist.hxx> +#include <unotools/charclass.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <tools/tenccvt.hxx> + +#include <com/sun/star/text/WritingMode2.hpp> + +#include "document.hxx" +#include "table.hxx" +#include "attrib.hxx" +#include "attarray.hxx" +#include "markarr.hxx" +#include "patattr.hxx" +#include "rangenam.hxx" +#include "poolhelp.hxx" +#include "docpool.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "globstr.hrc" +#include "rechead.hxx" +#include "dbcolect.hxx" +#include "pivot.hxx" +#include "chartlis.hxx" +#include "rangelst.hxx" +#include "markdata.hxx" +#include "drwlayer.hxx" +#include "conditio.hxx" +#include "validat.hxx" +#include "prnsave.hxx" +#include "chgtrack.hxx" +#include "sc.hrc" +#include "scresid.hxx" +#include "hints.hxx" +#include "detdata.hxx" +#include "cell.hxx" +#include "dpobject.hxx" +#include "detfunc.hxx" // for UpdateAllComments +#include "scmod.hxx" +#include "dociter.hxx" +#include "progress.hxx" +#include "autonamecache.hxx" +#include "bcaslot.hxx" +#include "postit.hxx" +#include "externalrefmgr.hxx" +#include "tabprotection.hxx" +#include "clipparam.hxx" + +#include <map> + +namespace WritingMode2 = ::com::sun::star::text::WritingMode2; + +struct ScDefaultAttr +{ + const ScPatternAttr* pAttr; + SCROW nFirst; + SCSIZE nCount; + ScDefaultAttr(const ScPatternAttr* pPatAttr) : pAttr(pPatAttr), nFirst(0), nCount(0) {} +}; + +struct ScLessDefaultAttr +{ + sal_Bool operator() (const ScDefaultAttr& rValue1, const ScDefaultAttr& rValue2) const + { + return rValue1.pAttr < rValue2.pAttr; + } +}; + +typedef std::set<ScDefaultAttr, ScLessDefaultAttr> ScDefaultAttrSet; + +void ScDocument::MakeTable( SCTAB nTab,bool _bNeedsNameCheck ) +{ + if ( ValidTab(nTab) && !pTab[nTab] ) + { + String aString = ScGlobal::GetRscString(STR_TABLE_DEF); //"Tabelle" + aString += String::CreateFromInt32(nTab+1); + if ( _bNeedsNameCheck ) + CreateValidTabName( aString ); // keine doppelten + + pTab[nTab] = new ScTable(this, nTab, aString); + ++nMaxTableNumber; + } +} + + +BOOL ScDocument::HasTable( SCTAB nTab ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return TRUE; + + return FALSE; +} + + +BOOL ScDocument::GetName( SCTAB nTab, String& rName ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + { + pTab[nTab]->GetName( rName ); + return TRUE; + } + rName.Erase(); + return FALSE; +} + + +BOOL ScDocument::GetTable( const String& rName, SCTAB& rTab ) const +{ + String aUpperName = rName; + ScGlobal::pCharClass->toUpper(aUpperName); + + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) + { + if ( pTab[i]->GetUpperName() == aUpperName ) + { + rTab = i; + return TRUE; + } + } + rTab = 0; + return FALSE; +} + + +BOOL ScDocument::ValidTabName( const String& rName ) const +{ + xub_StrLen nLen = rName.Len(); + if (!nLen) + return false; + +#if 1 + // Restrict sheet names to what Excel accepts. + /* TODO: We may want to remove this restriction for full ODFF compliance. + * Merely loading and calculating ODF documents using these characters in + * sheet names is not affected by this, but all sheet name editing and + * copying functionality is, maybe falling back to "Sheet4" or similar. */ + for (xub_StrLen i = 0; i < nLen; ++i) + { + const sal_Unicode c = rName.GetChar(i); + switch (c) + { + case ':': + case '\\': + case '/': + case '?': + case '*': + case '[': + case ']': + // these characters are not allowed to match XL's convention. + return false; + case '\'': + if (i == 0 || i == nLen - 1) + // single quote is not allowed at the first or last + // character position. + return false; + break; + } + } +#endif + + return true; +} + + +BOOL ScDocument::ValidNewTabName( const String& rName ) const +{ + BOOL bValid = ValidTabName(rName); + for (SCTAB i=0; (i<=MAXTAB) && bValid; i++) + if (pTab[i]) + { + String aOldName; + pTab[i]->GetName(aOldName); + bValid = !ScGlobal::GetpTransliteration()->isEqual( rName, aOldName ); + } + return bValid; +} + + +void ScDocument::CreateValidTabName(String& rName) const +{ + if ( !ValidTabName(rName) ) + { + // neu erzeugen + + const String aStrTable( ScResId(SCSTR_TABLE) ); + BOOL bOk = FALSE; + + // vorneweg testen, ob der Prefix als gueltig erkannt wird + // wenn nicht, nur doppelte vermeiden + BOOL bPrefix = ValidTabName( aStrTable ); + DBG_ASSERT(bPrefix, "ungueltiger Tabellenname"); + SCTAB nDummy; + + SCTAB nLoops = 0; // "zur Sicherheit" + for ( SCTAB i = nMaxTableNumber+1; !bOk && nLoops <= MAXTAB; i++ ) + { + rName = aStrTable; + rName += String::CreateFromInt32(i); + if (bPrefix) + bOk = ValidNewTabName( rName ); + else + bOk = !GetTable( rName, nDummy ); + ++nLoops; + } + + DBG_ASSERT(bOk, "kein gueltiger Tabellenname gefunden"); + if ( !bOk ) + rName = aStrTable; + } + else + { + // uebergebenen Namen ueberpruefen + + if ( !ValidNewTabName(rName) ) + { + SCTAB i = 1; + String aName; + do + { + i++; + aName = rName; + aName += '_'; + aName += String::CreateFromInt32(static_cast<sal_Int32>(i)); + } + while (!ValidNewTabName(aName) && (i < MAXTAB+1)); + rName = aName; + } + } +} + + +BOOL ScDocument::InsertTab( SCTAB nPos, const String& rName, + BOOL bExternalDocument ) +{ + SCTAB nTabCount = GetTableCount(); + BOOL bValid = ValidTab(nTabCount); + if ( !bExternalDocument ) // sonst rName == "'Doc'!Tab", vorher pruefen + bValid = (bValid && ValidNewTabName(rName)); + if (bValid) + { + if (nPos == SC_TAB_APPEND || nPos == nTabCount) + { + pTab[nTabCount] = new ScTable(this, nTabCount, rName); + ++nMaxTableNumber; + if ( bExternalDocument ) + pTab[nTabCount]->SetVisible( FALSE ); + } + else + { + if (VALIDTAB(nPos) && (nPos < nTabCount)) + { + ScRange aRange( 0,0,nPos, MAXCOL,MAXROW,MAXTAB ); + xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); + xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,1 ); + pRangeName->UpdateTabRef( nPos, 1 ); + pDBCollection->UpdateReference( + URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); + if (pDPCollection) + pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + if (pDetOpList) + pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,1 ); + UpdateChartRef( URM_INSDEL, 0,0,nPos, MAXCOL,MAXROW,MAXTAB, 0,0,1 ); + UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,1 ); + if ( pUnoBroadcaster ) + pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,1 ) ); + + SCTAB i; + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateInsertTab(nPos); + for (i = nTabCount; i > nPos; i--) + pTab[i] = pTab[i - 1]; + pTab[nPos] = new ScTable(this, nPos, rName); + ++nMaxTableNumber; + // UpdateBroadcastAreas must be called between UpdateInsertTab, + // which ends listening, and StartAllListeners, to not modify + // areas that are to be inserted by starting listeners. + UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,1); + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateCompile(); + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartAllListeners(); + + // update conditional formats after table is inserted + if ( pCondFormList ) + pCondFormList->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + if ( pValidationList ) + pValidationList->UpdateReference( URM_INSDEL, aRange, 0,0,1 ); + // #81844# sheet names of references are not valid until sheet is inserted + if ( pChartListenerCollection ) + pChartListenerCollection->UpdateScheduledSeriesRanges(); + + // Update cells containing external references. + if (pExternalRefMgr.get()) + pExternalRefMgr->updateRefInsertTable(nPos); + + SetDirty(); + bValid = TRUE; + } + else + bValid = FALSE; + } + } + return bValid; +} + + +BOOL ScDocument::DeleteTab( SCTAB nTab, ScDocument* pRefUndoDoc ) +{ + BOOL bValid = FALSE; + if (VALIDTAB(nTab)) + { + if (pTab[nTab]) + { + SCTAB nTabCount = GetTableCount(); + if (nTabCount > 1) + { + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + ScRange aRange( 0, 0, nTab, MAXCOL, MAXROW, nTab ); + DelBroadcastAreasInRange( aRange ); + + // #i8180# remove database ranges etc. that are on the deleted tab + // (restored in undo with ScRefUndoData) + + xColNameRanges->DeleteOnTab( nTab ); + xRowNameRanges->DeleteOnTab( nTab ); + pDBCollection->DeleteOnTab( nTab ); + if (pDPCollection) + pDPCollection->DeleteOnTab( nTab ); + if (pDetOpList) + pDetOpList->DeleteOnTab( nTab ); + DeleteAreaLinksOnTab( nTab ); + + // normal reference update + + aRange.aEnd.SetTab( MAXTAB ); + xColNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 ); + xRowNameRanges->UpdateReference( URM_INSDEL, this, aRange, 0,0,-1 ); + pRangeName->UpdateTabRef( nTab, 2 ); + pDBCollection->UpdateReference( + URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 ); + if (pDPCollection) + pDPCollection->UpdateReference( URM_INSDEL, aRange, 0,0,-1 ); + if (pDetOpList) + pDetOpList->UpdateReference( this, URM_INSDEL, aRange, 0,0,-1 ); + UpdateChartRef( URM_INSDEL, 0,0,nTab, MAXCOL,MAXROW,MAXTAB, 0,0,-1 ); + UpdateRefAreaLinks( URM_INSDEL, aRange, 0,0,-1 ); + if ( pCondFormList ) + pCondFormList->UpdateReference( URM_INSDEL, aRange, 0,0,-1 ); + if ( pValidationList ) + pValidationList->UpdateReference( URM_INSDEL, aRange, 0,0,-1 ); + if ( pUnoBroadcaster ) + pUnoBroadcaster->Broadcast( ScUpdateRefHint( URM_INSDEL, aRange, 0,0,-1 ) ); + + SCTAB i; + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateDeleteTab(nTab,FALSE, + pRefUndoDoc ? pRefUndoDoc->pTab[i] : 0); + delete pTab[nTab]; + for (i=nTab + 1; i < nTabCount; i++) + pTab[i - 1] = pTab[i]; + pTab[nTabCount - 1] = NULL; + --nMaxTableNumber; + // UpdateBroadcastAreas must be called between UpdateDeleteTab, + // which ends listening, and StartAllListeners, to not modify + // areas that are to be inserted by starting listeners. + UpdateBroadcastAreas( URM_INSDEL, aRange, 0,0,-1); + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->UpdateCompile(); + // Excel-Filter loescht einige Tables waehrend des Ladens, + // Listener werden erst nach dem Laden aufgesetzt + if ( !bInsertingFromOtherDoc ) + { + for (i = 0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartAllListeners(); + SetDirty(); + } + // #81844# sheet names of references are not valid until sheet is deleted + pChartListenerCollection->UpdateScheduledSeriesRanges(); + + + // Update cells containing external references. + if (pExternalRefMgr.get()) + pExternalRefMgr->updateRefDeleteTable(nTab); + + SetAutoCalc( bOldAutoCalc ); + bValid = TRUE; + } + } + } + return bValid; +} + + +BOOL ScDocument::RenameTab( SCTAB nTab, const String& rName, BOOL /* bUpdateRef */, + BOOL bExternalDocument ) +{ + BOOL bValid = FALSE; + SCTAB i; + if VALIDTAB(nTab) + if (pTab[nTab]) + { + if ( bExternalDocument ) + bValid = TRUE; // zusammengesetzter Name + else + bValid = ValidTabName(rName); + for (i=0; (i<=MAXTAB) && bValid; i++) + if (pTab[i] && (i != nTab)) + { + String aOldName; + pTab[i]->GetName(aOldName); + bValid = !ScGlobal::GetpTransliteration()->isEqual( rName, aOldName ); + } + if (bValid) + { + // #i75258# update charts before renaming, so they can get their live data objects. + // Once the charts are live, the sheet can be renamed without problems. + if ( pChartListenerCollection ) + pChartListenerCollection->UpdateChartsContainingTab( nTab ); + pTab[nTab]->SetName(rName); + + // If formulas refer to the renamed sheet, the TokenArray remains valid, + // but the XML stream must be re-generated. + for (i=0; i<=MAXTAB; ++i) + if (pTab[i] && pTab[i]->IsStreamValid()) + pTab[i]->SetStreamValid( FALSE ); + } + } + return bValid; +} + + +void ScDocument::SetVisible( SCTAB nTab, BOOL bVisible ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->SetVisible(bVisible); +} + + +BOOL ScDocument::IsVisible( SCTAB nTab ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->IsVisible(); + + return FALSE; +} + + +BOOL ScDocument::IsStreamValid( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->IsStreamValid(); + + return FALSE; +} + + +void ScDocument::SetStreamValid( SCTAB nTab, BOOL bSet, BOOL bIgnoreLock ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetStreamValid( bSet, bIgnoreLock ); +} + + +void ScDocument::LockStreamValid( bool bLock ) +{ + mbStreamValidLocked = bLock; +} + + +BOOL ScDocument::IsPendingRowHeights( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->IsPendingRowHeights(); + + return FALSE; +} + + +void ScDocument::SetPendingRowHeights( SCTAB nTab, BOOL bSet ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetPendingRowHeights( bSet ); +} + + +void ScDocument::SetLayoutRTL( SCTAB nTab, BOOL bRTL ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + { + if ( bImportingXML ) + { + // #i57869# only set the LoadingRTL flag, the real setting (including mirroring) + // is applied in SetImportingXML(FALSE). This is so the shapes can be loaded in + // normal LTR mode. + + pTab[nTab]->SetLoadingRTL( bRTL ); + return; + } + + pTab[nTab]->SetLayoutRTL( bRTL ); // only sets the flag + pTab[nTab]->SetDrawPageSize(); + + // mirror existing objects: + + if (pDrawLayer) + { + SdrPage* pPage = pDrawLayer->GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPNOGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + // objects with ScDrawObjData are re-positioned in SetPageSize, + // don't mirror again + ScDrawObjData* pData = ScDrawLayer::GetObjData( pObject ); + if ( !pData ) + pDrawLayer->MirrorRTL( pObject ); + + pObject->SetContextWritingMode( bRTL ? WritingMode2::RL_TB : WritingMode2::LR_TB ); + + pObject = aIter.Next(); + } + } + } + } +} + + +BOOL ScDocument::IsLayoutRTL( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->IsLayoutRTL(); + + return FALSE; +} + + +BOOL ScDocument::IsNegativePage( SCTAB nTab ) const +{ + // Negative page area is always used for RTL layout. + // The separate method is used to find all RTL handling of drawing objects. + return IsLayoutRTL( nTab ); +} + + +/* ---------------------------------------------------------------------------- + benutzten Bereich suchen: + + GetCellArea - nur Daten + GetTableArea - Daten / Attribute + GetPrintArea - beruecksichtigt auch Zeichenobjekte, + streicht Attribute bis ganz rechts / unten +---------------------------------------------------------------------------- */ + + +BOOL ScDocument::GetCellArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->GetCellArea( rEndCol, rEndRow ); + + rEndCol = 0; + rEndRow = 0; + return FALSE; +} + + +BOOL ScDocument::GetTableArea( SCTAB nTab, SCCOL& rEndCol, SCROW& rEndRow ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->GetTableArea( rEndCol, rEndRow ); + + rEndCol = 0; + rEndRow = 0; + return FALSE; +} + + +// zusammenhaengender Bereich + +void ScDocument::GetDataArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, + SCCOL& rEndCol, SCROW& rEndRow, BOOL bIncludeOld ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->GetDataArea( rStartCol, rStartRow, rEndCol, rEndRow, bIncludeOld ); +} + + +void ScDocument::LimitChartArea( SCTAB nTab, SCCOL& rStartCol, SCROW& rStartRow, + SCCOL& rEndCol, SCROW& rEndRow ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->LimitChartArea( rStartCol, rStartRow, rEndCol, rEndRow ); +} + + +void ScDocument::LimitChartIfAll( ScRangeListRef& rRangeList ) +{ + ScRangeListRef aNew = new ScRangeList; + if (rRangeList.Is()) + { + ULONG nCount = rRangeList->Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange aRange(*rRangeList->GetObject( i )); + if ( ( aRange.aStart.Col() == 0 && aRange.aEnd.Col() == MAXCOL ) || + ( aRange.aStart.Row() == 0 && aRange.aEnd.Row() == MAXROW ) ) + { + SCCOL nStartCol = aRange.aStart.Col(); + SCROW nStartRow = aRange.aStart.Row(); + SCCOL nEndCol = aRange.aEnd.Col(); + SCROW nEndRow = aRange.aEnd.Row(); + SCTAB nTab = aRange.aStart.Tab(); + if (pTab[nTab]) + pTab[nTab]->LimitChartArea(nStartCol, nStartRow, nEndCol, nEndRow); + aRange.aStart.SetCol( nStartCol ); + aRange.aStart.SetRow( nStartRow ); + aRange.aEnd.SetCol( nEndCol ); + aRange.aEnd.SetRow( nEndRow ); + } + aNew->Append(aRange); + } + } + else + { + DBG_ERROR("LimitChartIfAll: Ref==0"); + } + rRangeList = aNew; +} + + +void lcl_GetFirstTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark ) +{ + // without ScMarkData, leave start/end unchanged + if ( pTabMark ) + { + for (SCTAB nTab=0; nTab<=MAXTAB; ++nTab) + if (pTabMark->GetTableSelect(nTab)) + { + // find first range of consecutive selected sheets + rTabRangeStart = nTab; + while ( nTab+1 <= MAXTAB && pTabMark->GetTableSelect(nTab+1) ) + ++nTab; + rTabRangeEnd = nTab; + return; + } + } +} + +bool lcl_GetNextTabRange( SCTAB& rTabRangeStart, SCTAB& rTabRangeEnd, const ScMarkData* pTabMark ) +{ + if ( pTabMark ) + { + // find next range of consecutive selected sheets after rTabRangeEnd + for (SCTAB nTab=rTabRangeEnd+1; nTab<=MAXTAB; ++nTab) + if (pTabMark->GetTableSelect(nTab)) + { + rTabRangeStart = nTab; + while ( nTab+1 <= MAXTAB && pTabMark->GetTableSelect(nTab+1) ) + ++nTab; + rTabRangeEnd = nTab; + return true; + } + } + return false; +} + + +BOOL ScDocument::CanInsertRow( const ScRange& rRange ) const +{ + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCTAB nStartTab = rRange.aStart.Tab(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + SCTAB nEndTab = rRange.aEnd.Tab(); + PutInOrder( nStartCol, nEndCol ); + PutInOrder( nStartRow, nEndRow ); + PutInOrder( nStartTab, nEndTab ); + SCSIZE nSize = static_cast<SCSIZE>(nEndRow - nStartRow + 1); + + BOOL bTest = TRUE; + for (SCTAB i=nStartTab; i<=nEndTab && bTest; i++) + if (pTab[i]) + bTest &= pTab[i]->TestInsertRow( nStartCol, nEndCol, nSize ); + + return bTest; +} + + +BOOL ScDocument::InsertRow( SCCOL nStartCol, SCTAB nStartTab, + SCCOL nEndCol, SCTAB nEndTab, + SCROW nStartRow, SCSIZE nSize, ScDocument* pRefUndoDoc, + const ScMarkData* pTabMark ) +{ + SCTAB i; + + PutInOrder( nStartCol, nEndCol ); + PutInOrder( nStartTab, nEndTab ); + if ( pTabMark ) + { + nStartTab = 0; + nEndTab = MAXTAB; + } + + BOOL bTest = TRUE; + BOOL bRet = FALSE; + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for ( i = nStartTab; i <= nEndTab && bTest; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + bTest &= pTab[i]->TestInsertRow( nStartCol, nEndCol, nSize ); + if (bTest) + { + // UpdateBroadcastAreas muss vor UpdateReference gerufen werden, damit nicht + // Eintraege verschoben werden, die erst bei UpdateReference neu erzeugt werden + + // handle chunks of consecutive selected sheets together + SCTAB nTabRangeStart = nStartTab; + SCTAB nTabRangeEnd = nEndTab; + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateBroadcastAreas( URM_INSDEL, ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( nEndCol, MAXROW, nTabRangeEnd )), 0, static_cast<SCsROW>(nSize), 0 ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateReference( URM_INSDEL, nStartCol, nStartRow, nTabRangeStart, + nEndCol, MAXROW, nTabRangeEnd, + 0, static_cast<SCsROW>(nSize), 0, pRefUndoDoc, FALSE ); // without drawing objects + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + for (i=nStartTab; i<=nEndTab; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + pTab[i]->InsertRow( nStartCol, nEndCol, nStartRow, nSize ); + + // #82991# UpdateRef for drawing layer must be after inserting, + // when the new row heights are known. + for (i=nStartTab; i<=nEndTab; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + pTab[i]->UpdateDrawRef( URM_INSDEL, + nStartCol, nStartRow, nStartTab, nEndCol, MAXROW, nEndTab, + 0, static_cast<SCsROW>(nSize), 0 ); + + if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) + { // durch Restaurierung von Referenzen auf geloeschte Bereiche ist + // ein neues Listening faellig, bisherige Listener wurden in + // FormulaCell UpdateReference abgehaengt + StartAllListeners(); + } + else + { // Listeners have been removed in UpdateReference + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartNeededListeners(); + // #69592# at least all cells using range names pointing relative + // to the moved range must recalculate + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->SetRelNameDirty(); + } + bRet = TRUE; + } + SetAutoCalc( bOldAutoCalc ); + if ( bRet ) + pChartListenerCollection->UpdateDirtyCharts(); + return bRet; +} + + +BOOL ScDocument::InsertRow( const ScRange& rRange, ScDocument* pRefUndoDoc ) +{ + return InsertRow( rRange.aStart.Col(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Tab(), + rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1), + pRefUndoDoc ); +} + + +void ScDocument::DeleteRow( SCCOL nStartCol, SCTAB nStartTab, + SCCOL nEndCol, SCTAB nEndTab, + SCROW nStartRow, SCSIZE nSize, + ScDocument* pRefUndoDoc, BOOL* pUndoOutline, + const ScMarkData* pTabMark ) +{ + SCTAB i; + + PutInOrder( nStartCol, nEndCol ); + PutInOrder( nStartTab, nEndTab ); + if ( pTabMark ) + { + nStartTab = 0; + nEndTab = MAXTAB; + } + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + // handle chunks of consecutive selected sheets together + SCTAB nTabRangeStart = nStartTab; + SCTAB nTabRangeEnd = nEndTab; + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + if ( ValidRow(nStartRow+nSize) ) + { + DelBroadcastAreasInRange( ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( nEndCol, nStartRow+nSize-1, nTabRangeEnd ) ) ); + UpdateBroadcastAreas( URM_INSDEL, ScRange( + ScAddress( nStartCol, nStartRow+nSize, nTabRangeStart ), + ScAddress( nEndCol, MAXROW, nTabRangeEnd )), 0, -(static_cast<SCsROW>(nSize)), 0 ); + } + else + DelBroadcastAreasInRange( ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( nEndCol, MAXROW, nTabRangeEnd ) ) ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + if ( ValidRow(nStartRow+nSize) ) + { + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateReference( URM_INSDEL, nStartCol, nStartRow+nSize, nTabRangeStart, + nEndCol, MAXROW, nTabRangeEnd, + 0, -(static_cast<SCsROW>(nSize)), 0, pRefUndoDoc ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + } + + if (pUndoOutline) + *pUndoOutline = FALSE; + + for ( i = nStartTab; i <= nEndTab; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + pTab[i]->DeleteRow( nStartCol, nEndCol, nStartRow, nSize, pUndoOutline ); + + if ( ValidRow(nStartRow+nSize) ) + { // Listeners have been removed in UpdateReference + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartNeededListeners(); + // #69592# at least all cells using range names pointing relative to + // the moved range must recalculate + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->SetRelNameDirty(); + } + + SetAutoCalc( bOldAutoCalc ); + pChartListenerCollection->UpdateDirtyCharts(); +} + + +void ScDocument::DeleteRow( const ScRange& rRange, ScDocument* pRefUndoDoc, BOOL* pUndoOutline ) +{ + DeleteRow( rRange.aStart.Col(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Tab(), + rRange.aStart.Row(), static_cast<SCSIZE>(rRange.aEnd.Row()-rRange.aStart.Row()+1), + pRefUndoDoc, pUndoOutline ); +} + + +BOOL ScDocument::CanInsertCol( const ScRange& rRange ) const +{ + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCTAB nStartTab = rRange.aStart.Tab(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + SCTAB nEndTab = rRange.aEnd.Tab(); + PutInOrder( nStartCol, nEndCol ); + PutInOrder( nStartRow, nEndRow ); + PutInOrder( nStartTab, nEndTab ); + SCSIZE nSize = static_cast<SCSIZE>(nEndCol - nStartCol + 1); + + BOOL bTest = TRUE; + for (SCTAB i=nStartTab; i<=nEndTab && bTest; i++) + if (pTab[i]) + bTest &= pTab[i]->TestInsertCol( nStartRow, nEndRow, nSize ); + + return bTest; +} + + +BOOL ScDocument::InsertCol( SCROW nStartRow, SCTAB nStartTab, + SCROW nEndRow, SCTAB nEndTab, + SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc, + const ScMarkData* pTabMark ) +{ + SCTAB i; + + PutInOrder( nStartRow, nEndRow ); + PutInOrder( nStartTab, nEndTab ); + if ( pTabMark ) + { + nStartTab = 0; + nEndTab = MAXTAB; + } + + BOOL bTest = TRUE; + BOOL bRet = FALSE; + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for ( i = nStartTab; i <= nEndTab && bTest; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + bTest &= pTab[i]->TestInsertCol( nStartRow, nEndRow, nSize ); + if (bTest) + { + // handle chunks of consecutive selected sheets together + SCTAB nTabRangeStart = nStartTab; + SCTAB nTabRangeEnd = nEndTab; + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateBroadcastAreas( URM_INSDEL, ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), static_cast<SCsCOL>(nSize), 0, 0 ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateReference( URM_INSDEL, nStartCol, nStartRow, nTabRangeStart, + MAXCOL, nEndRow, nTabRangeEnd, + static_cast<SCsCOL>(nSize), 0, 0, pRefUndoDoc ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + for (i=nStartTab; i<=nEndTab; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + pTab[i]->InsertCol( nStartCol, nStartRow, nEndRow, nSize ); + + if ( pChangeTrack && pChangeTrack->IsInDeleteUndo() ) + { // durch Restaurierung von Referenzen auf geloeschte Bereiche ist + // ein neues Listening faellig, bisherige Listener wurden in + // FormulaCell UpdateReference abgehaengt + StartAllListeners(); + } + else + { // Listeners have been removed in UpdateReference + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartNeededListeners(); + // #69592# at least all cells using range names pointing relative + // to the moved range must recalculate + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->SetRelNameDirty(); + } + bRet = TRUE; + } + SetAutoCalc( bOldAutoCalc ); + if ( bRet ) + pChartListenerCollection->UpdateDirtyCharts(); + return bRet; +} + + +BOOL ScDocument::InsertCol( const ScRange& rRange, ScDocument* pRefUndoDoc ) +{ + return InsertCol( rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Row(), rRange.aEnd.Tab(), + rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1), + pRefUndoDoc ); +} + + +void ScDocument::DeleteCol(SCROW nStartRow, SCTAB nStartTab, SCROW nEndRow, SCTAB nEndTab, + SCCOL nStartCol, SCSIZE nSize, ScDocument* pRefUndoDoc, + BOOL* pUndoOutline, const ScMarkData* pTabMark ) +{ + SCTAB i; + + PutInOrder( nStartRow, nEndRow ); + PutInOrder( nStartTab, nEndTab ); + if ( pTabMark ) + { + nStartTab = 0; + nEndTab = MAXTAB; + } + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + // handle chunks of consecutive selected sheets together + SCTAB nTabRangeStart = nStartTab; + SCTAB nTabRangeEnd = nEndTab; + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) ) + { + DelBroadcastAreasInRange( ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize-1), nEndRow, nTabRangeEnd ) ) ); + UpdateBroadcastAreas( URM_INSDEL, ScRange( + ScAddress( sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart ), + ScAddress( MAXCOL, nEndRow, nTabRangeEnd )), -static_cast<SCsCOL>(nSize), 0, 0 ); + } + else + DelBroadcastAreasInRange( ScRange( + ScAddress( nStartCol, nStartRow, nTabRangeStart ), + ScAddress( MAXCOL, nEndRow, nTabRangeEnd ) ) ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + + if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) ) + { + lcl_GetFirstTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ); + do + { + UpdateReference( URM_INSDEL, sal::static_int_cast<SCCOL>(nStartCol+nSize), nStartRow, nTabRangeStart, + MAXCOL, nEndRow, nTabRangeEnd, + -static_cast<SCsCOL>(nSize), 0, 0, pRefUndoDoc ); + } + while ( lcl_GetNextTabRange( nTabRangeStart, nTabRangeEnd, pTabMark ) ); + } + + if (pUndoOutline) + *pUndoOutline = FALSE; + + for ( i = nStartTab; i <= nEndTab; i++) + if (pTab[i] && (!pTabMark || pTabMark->GetTableSelect(i))) + pTab[i]->DeleteCol( nStartCol, nStartRow, nEndRow, nSize, pUndoOutline ); + + if ( ValidCol(sal::static_int_cast<SCCOL>(nStartCol+nSize)) ) + { // Listeners have been removed in UpdateReference + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->StartNeededListeners(); + // #69592# at least all cells using range names pointing relative to + // the moved range must recalculate + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) + pTab[i]->SetRelNameDirty(); + } + + SetAutoCalc( bOldAutoCalc ); + pChartListenerCollection->UpdateDirtyCharts(); +} + + +void ScDocument::DeleteCol( const ScRange& rRange, ScDocument* pRefUndoDoc, BOOL* pUndoOutline ) +{ + DeleteCol( rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Row(), rRange.aEnd.Tab(), + rRange.aStart.Col(), static_cast<SCSIZE>(rRange.aEnd.Col()-rRange.aStart.Col()+1), + pRefUndoDoc, pUndoOutline ); +} + + +// fuer Area-Links: Zellen einuegen/loeschen, wenn sich der Bereich veraendert +// (ohne Paint) + + +void lcl_GetInsDelRanges( const ScRange& rOld, const ScRange& rNew, + ScRange& rColRange, BOOL& rInsCol, BOOL& rDelCol, + ScRange& rRowRange, BOOL& rInsRow, BOOL& rDelRow ) +{ + DBG_ASSERT( rOld.aStart == rNew.aStart, "FitBlock: Anfang unterschiedlich" ); + + rInsCol = rDelCol = rInsRow = rDelRow = FALSE; + + SCCOL nStartX = rOld.aStart.Col(); + SCROW nStartY = rOld.aStart.Row(); + SCCOL nOldEndX = rOld.aEnd.Col(); + SCROW nOldEndY = rOld.aEnd.Row(); + SCCOL nNewEndX = rNew.aEnd.Col(); + SCROW nNewEndY = rNew.aEnd.Row(); + SCTAB nTab = rOld.aStart.Tab(); + + // wenn es mehr Zeilen werden, werden Spalten auf der alten Hoehe eingefuegt/geloescht + BOOL bGrowY = ( nNewEndY > nOldEndY ); + SCROW nColEndY = bGrowY ? nOldEndY : nNewEndY; + SCCOL nRowEndX = bGrowY ? nNewEndX : nOldEndX; + + // Spalten + + if ( nNewEndX > nOldEndX ) // Spalten einfuegen + { + rColRange = ScRange( nOldEndX+1, nStartY, nTab, nNewEndX, nColEndY, nTab ); + rInsCol = TRUE; + } + else if ( nNewEndX < nOldEndX ) // Spalten loeschen + { + rColRange = ScRange( nNewEndX+1, nStartY, nTab, nOldEndX, nColEndY, nTab ); + rDelCol = TRUE; + } + + // Zeilen + + if ( nNewEndY > nOldEndY ) // Zeilen einfuegen + { + rRowRange = ScRange( nStartX, nOldEndY+1, nTab, nRowEndX, nNewEndY, nTab ); + rInsRow = TRUE; + } + else if ( nNewEndY < nOldEndY ) // Zeilen loeschen + { + rRowRange = ScRange( nStartX, nNewEndY+1, nTab, nRowEndX, nOldEndY, nTab ); + rDelRow = TRUE; + } +} + + +BOOL ScDocument::HasPartOfMerged( const ScRange& rRange ) +{ + BOOL bPart = FALSE; + SCTAB nTab = rRange.aStart.Tab(); + + SCCOL nStartX = rRange.aStart.Col(); + SCROW nStartY = rRange.aStart.Row(); + SCCOL nEndX = rRange.aEnd.Col(); + SCROW nEndY = rRange.aEnd.Row(); + + if (HasAttrib( nStartX, nStartY, nTab, nEndX, nEndY, nTab, + HASATTR_MERGED | HASATTR_OVERLAPPED )) + { + ExtendMerge( nStartX, nStartY, nEndX, nEndY, nTab ); + ExtendOverlapped( nStartX, nStartY, nEndX, nEndY, nTab ); + + bPart = ( nStartX != rRange.aStart.Col() || nEndX != rRange.aEnd.Col() || + nStartY != rRange.aStart.Row() || nEndY != rRange.aEnd.Row() ); + } + return bPart; +} + + +BOOL ScDocument::CanFitBlock( const ScRange& rOld, const ScRange& rNew ) +{ + if ( rOld == rNew ) + return TRUE; + + BOOL bOk = TRUE; + BOOL bInsCol,bDelCol,bInsRow,bDelRow; + ScRange aColRange,aRowRange; + lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow ); + + if ( bInsCol && !CanInsertCol( aColRange ) ) // Zellen am Rand ? + bOk = FALSE; + if ( bInsRow && !CanInsertRow( aRowRange ) ) // Zellen am Rand ? + bOk = FALSE; + + if ( bInsCol || bDelCol ) + { + aColRange.aEnd.SetCol(MAXCOL); + if ( HasPartOfMerged(aColRange) ) + bOk = FALSE; + } + if ( bInsRow || bDelRow ) + { + aRowRange.aEnd.SetRow(MAXROW); + if ( HasPartOfMerged(aRowRange) ) + bOk = FALSE; + } + + return bOk; +} + + +void ScDocument::FitBlock( const ScRange& rOld, const ScRange& rNew, BOOL bClear ) +{ + if (bClear) + DeleteAreaTab( rOld, IDF_ALL ); + + BOOL bInsCol,bDelCol,bInsRow,bDelRow; + ScRange aColRange,aRowRange; + lcl_GetInsDelRanges( rOld, rNew, aColRange,bInsCol,bDelCol, aRowRange,bInsRow,bDelRow ); + + if ( bInsCol ) + InsertCol( aColRange ); // Spalten zuerst einfuegen + if ( bInsRow ) + InsertRow( aRowRange ); + + if ( bDelRow ) + DeleteRow( aRowRange ); // Zeilen zuerst loeschen + if ( bDelCol ) + DeleteCol( aColRange ); + + // Referenzen um eingefuegte Zeilen erweitern + + if ( bInsCol || bInsRow ) + { + ScRange aGrowSource = rOld; + aGrowSource.aEnd.SetCol(Min( rOld.aEnd.Col(), rNew.aEnd.Col() )); + aGrowSource.aEnd.SetRow(Min( rOld.aEnd.Row(), rNew.aEnd.Row() )); + SCCOL nGrowX = bInsCol ? ( rNew.aEnd.Col() - rOld.aEnd.Col() ) : 0; + SCROW nGrowY = bInsRow ? ( rNew.aEnd.Row() - rOld.aEnd.Row() ) : 0; + UpdateGrow( aGrowSource, nGrowX, nGrowY ); + } +} + + +void ScDocument::DeleteArea(SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark, USHORT nDelFlag) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCTAB i = 0; i <= MAXTAB; i++) + if (pTab[i]) + if ( rMark.GetTableSelect(i) || bIsUndo ) + pTab[i]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag); + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::DeleteAreaTab(SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + SCTAB nTab, USHORT nDelFlag) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + if ( VALIDTAB(nTab) && pTab[nTab] ) + { + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + pTab[nTab]->DeleteArea(nCol1, nRow1, nCol2, nRow2, nDelFlag); + SetAutoCalc( bOldAutoCalc ); + } +} + + +void ScDocument::DeleteAreaTab( const ScRange& rRange, USHORT nDelFlag ) +{ + for ( SCTAB nTab = rRange.aStart.Tab(); nTab <= rRange.aEnd.Tab(); nTab++ ) + DeleteAreaTab( rRange.aStart.Col(), rRange.aStart.Row(), + rRange.aEnd.Col(), rRange.aEnd.Row(), + nTab, nDelFlag ); +} + + +void ScDocument::InitUndoSelected( ScDocument* pSrcDoc, const ScMarkData& rTabSelection, + BOOL bColInfo, BOOL bRowInfo ) +{ + if (bIsUndo) + { + Clear(); + + xPoolHelper = pSrcDoc->xPoolHelper; + + String aString; + for (SCTAB nTab = 0; nTab <= MAXTAB; nTab++) + if ( rTabSelection.GetTableSelect( nTab ) ) + { + pTab[nTab] = new ScTable(this, nTab, aString, bColInfo, bRowInfo); + nMaxTableNumber = nTab + 1; + } + } + else + { + DBG_ERROR("InitUndo"); + } +} + + +void ScDocument::InitUndo( ScDocument* pSrcDoc, SCTAB nTab1, SCTAB nTab2, + BOOL bColInfo, BOOL bRowInfo ) +{ + if (bIsUndo) + { + Clear(); + + xPoolHelper = pSrcDoc->xPoolHelper; + + String aString; + for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++) + pTab[nTab] = new ScTable(this, nTab, aString, bColInfo, bRowInfo); + + nMaxTableNumber = nTab2 + 1; + } + else + { + DBG_ERROR("InitUndo"); + } +} + + +void ScDocument::AddUndoTab( SCTAB nTab1, SCTAB nTab2, BOOL bColInfo, BOOL bRowInfo ) +{ + if (bIsUndo) + { + String aString; + for (SCTAB nTab = nTab1; nTab <= nTab2; nTab++) + if (!pTab[nTab]) + pTab[nTab] = new ScTable(this, nTab, aString, bColInfo, bRowInfo); + + if ( nMaxTableNumber <= nTab2 ) + nMaxTableNumber = nTab2 + 1; + } + else + { + DBG_ERROR("InitUndo"); + } +} + + +void ScDocument::SetCutMode( BOOL bVal ) +{ + if (bIsClip) + GetClipParam().mbCutMode = bVal; + else + { + DBG_ERROR("SetCutMode without bIsClip"); + } +} + + +BOOL ScDocument::IsCutMode() +{ + if (bIsClip) + return GetClipParam().mbCutMode; + else + { + DBG_ERROR("IsCutMode ohne bIsClip"); + return FALSE; + } +} + + +void ScDocument::CopyToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + USHORT nFlags, BOOL bOnlyMarked, ScDocument* pDestDoc, + const ScMarkData* pMarks, BOOL bColRowFlags ) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + PutInOrder( nTab1, nTab2 ); + if( !pDestDoc->aDocName.Len() ) + pDestDoc->aDocName = aDocName; + if (VALIDTAB(nTab1) && VALIDTAB(nTab2)) + { + BOOL bOldAutoCalc = pDestDoc->GetAutoCalc(); + pDestDoc->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCTAB i = nTab1; i <= nTab2; i++) + { + if (pTab[i] && pDestDoc->pTab[i]) + pTab[i]->CopyToTable( nCol1, nRow1, nCol2, nRow2, nFlags, + bOnlyMarked, pDestDoc->pTab[i], pMarks, + FALSE, bColRowFlags ); + } + pDestDoc->SetAutoCalc( bOldAutoCalc ); + } +} + + +void ScDocument::UndoToDocument(SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + USHORT nFlags, BOOL bOnlyMarked, ScDocument* pDestDoc, + const ScMarkData* pMarks) +{ + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + PutInOrder( nTab1, nTab2 ); + if (VALIDTAB(nTab1) && VALIDTAB(nTab2)) + { + BOOL bOldAutoCalc = pDestDoc->GetAutoCalc(); + pDestDoc->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + if (nTab1 > 0) + CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTab1-1, IDF_FORMULA, FALSE, pDestDoc, pMarks ); + + for (SCTAB i = nTab1; i <= nTab2; i++) + { + if (pTab[i] && pDestDoc->pTab[i]) + pTab[i]->UndoToTable(nCol1, nRow1, nCol2, nRow2, nFlags, + bOnlyMarked, pDestDoc->pTab[i], pMarks); + } + + if (nTab2 < MAXTAB) + CopyToDocument( 0,0,nTab2+1, MAXCOL,MAXROW,MAXTAB, IDF_FORMULA, FALSE, pDestDoc, pMarks ); + pDestDoc->SetAutoCalc( bOldAutoCalc ); + } +} + + +void ScDocument::CopyToDocument(const ScRange& rRange, + USHORT nFlags, BOOL bOnlyMarked, ScDocument* pDestDoc, + const ScMarkData* pMarks, BOOL bColRowFlags) +{ + ScRange aNewRange = rRange; + aNewRange.Justify(); + + if( !pDestDoc->aDocName.Len() ) + pDestDoc->aDocName = aDocName; + BOOL bOldAutoCalc = pDestDoc->GetAutoCalc(); + pDestDoc->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCTAB i = aNewRange.aStart.Tab(); i <= aNewRange.aEnd.Tab(); i++) + if (pTab[i] && pDestDoc->pTab[i]) + pTab[i]->CopyToTable(aNewRange.aStart.Col(), aNewRange.aStart.Row(), + aNewRange.aEnd.Col(), aNewRange.aEnd.Row(), + nFlags, bOnlyMarked, pDestDoc->pTab[i], + pMarks, FALSE, bColRowFlags); + pDestDoc->SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::UndoToDocument(const ScRange& rRange, + USHORT nFlags, BOOL bOnlyMarked, ScDocument* pDestDoc, + const ScMarkData* pMarks) +{ + ScRange aNewRange = rRange; + aNewRange.Justify(); + SCTAB nTab1 = aNewRange.aStart.Tab(); + SCTAB nTab2 = aNewRange.aEnd.Tab(); + + BOOL bOldAutoCalc = pDestDoc->GetAutoCalc(); + pDestDoc->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + if (nTab1 > 0) + CopyToDocument( 0,0,0, MAXCOL,MAXROW,nTab1-1, IDF_FORMULA, FALSE, pDestDoc, pMarks ); + + for (SCTAB i = nTab1; i <= nTab2; i++) + { + if (pTab[i] && pDestDoc->pTab[i]) + pTab[i]->UndoToTable(aNewRange.aStart.Col(), aNewRange.aStart.Row(), + aNewRange.aEnd.Col(), aNewRange.aEnd.Row(), + nFlags, bOnlyMarked, pDestDoc->pTab[i], pMarks); + } + + if (nTab2 < MAXTAB) + CopyToDocument( 0,0,nTab2+1, MAXCOL,MAXROW,MAXTAB, IDF_FORMULA, FALSE, pDestDoc, pMarks ); + pDestDoc->SetAutoCalc( bOldAutoCalc ); +} + +void ScDocument::CopyToClip(const ScClipParam& rClipParam, + ScDocument* pClipDoc, const ScMarkData* pMarks, + bool bAllTabs, bool bKeepScenarioFlags, bool bIncludeObjects, bool bCloneNoteCaptions) +{ + DBG_ASSERT( bAllTabs || pMarks, "CopyToClip: ScMarkData fehlt" ); + + if (bIsClip) + return; + + if (!pClipDoc) + { + DBG_ERROR("CopyToClip: no ClipDoc"); + pClipDoc = SC_MOD()->GetClipDoc(); + } + + pClipDoc->aDocName = aDocName; + pClipDoc->SetClipParam(rClipParam); + pClipDoc->ResetClip(this, pMarks); + + ScRange aClipRange = rClipParam.getWholeRange(); + CopyRangeNamesToClip(pClipDoc, aClipRange, pMarks, bAllTabs); + + for (SCTAB i = 0; i <= MAXTAB; ++i) + { + if (!pTab[i] || !pClipDoc->pTab[i]) + continue; + + if (pMarks && !pMarks->GetTableSelect(i)) + continue; + + pTab[i]->CopyToClip(rClipParam.maRanges, pClipDoc->pTab[i], bKeepScenarioFlags, bCloneNoteCaptions); + + if (pDrawLayer && bIncludeObjects) + { + // also copy drawing objects + Rectangle aObjRect = GetMMRect( + aClipRange.aStart.Col(), aClipRange.aStart.Row(), aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i); + pDrawLayer->CopyToClip(pClipDoc, i, aObjRect); + } + } + + // Make sure to mark overlapped cells. + pClipDoc->ExtendMerge(aClipRange, true); +} + +void ScDocument::CopyTabToClip(SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + SCTAB nTab, ScDocument* pClipDoc) +{ + if (!bIsClip) + { + PutInOrder( nCol1, nCol2 ); + PutInOrder( nRow1, nRow2 ); + if (!pClipDoc) + { + DBG_ERROR("CopyTabToClip: no ClipDoc"); + pClipDoc = SC_MOD()->GetClipDoc(); + } + + ScClipParam& rClipParam = pClipDoc->GetClipParam(); + pClipDoc->aDocName = aDocName; + rClipParam.maRanges.RemoveAll(); + rClipParam.maRanges.Append(ScRange(nCol1, nRow1, 0, nCol2, nRow2, 0)); + pClipDoc->ResetClip( this, nTab ); + + if (pTab[nTab] && pClipDoc->pTab[nTab]) + pTab[nTab]->CopyToClip(nCol1, nRow1, nCol2, nRow2, pClipDoc->pTab[nTab], FALSE, TRUE); + + pClipDoc->GetClipParam().mbCutMode = false; + } +} + + +void ScDocument::TransposeClip( ScDocument* pTransClip, USHORT nFlags, BOOL bAsLink ) +{ + DBG_ASSERT( bIsClip && pTransClip && pTransClip->bIsClip, + "TransposeClip mit falschem Dokument" ); + + // initialisieren + // -> pTransClip muss vor dem Original-Dokument geloescht werden! + + pTransClip->ResetClip(this, (ScMarkData*)NULL); // alle + + // Bereiche uebernehmen + + pTransClip->pRangeName->FreeAll(); + for (USHORT i = 0; i < pRangeName->GetCount(); i++) //! DB-Bereiche Pivot-Bereiche auch !!! + { + USHORT nIndex = ((ScRangeData*)((*pRangeName)[i]))->GetIndex(); + ScRangeData* pData = new ScRangeData(*((*pRangeName)[i])); + if (!pTransClip->pRangeName->Insert(pData)) + delete pData; + else + pData->SetIndex(nIndex); + } + + // Daten + + ScRange aClipRange = GetClipParam().getWholeRange(); + if ( ValidRow(aClipRange.aEnd.Row()-aClipRange.aStart.Row()) ) + { + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) + { + DBG_ASSERT( pTransClip->pTab[i], "TransposeClip: Tabelle nicht da" ); + pTab[i]->TransposeClip( aClipRange.aStart.Col(), aClipRange.aStart.Row(), + aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), + pTransClip->pTab[i], nFlags, bAsLink ); + + if ( pDrawLayer && ( nFlags & IDF_OBJECTS ) ) + { + // Drawing objects are copied to the new area without transposing. + // CopyFromClip is used to adjust the objects to the transposed block's + // cell range area. + // (pDrawLayer in the original clipboard document is set only if there + // are drawing objects to copy) + + pTransClip->InitDrawLayer(); + Rectangle aSourceRect = GetMMRect( aClipRange.aStart.Col(), aClipRange.aStart.Row(), + aClipRange.aEnd.Col(), aClipRange.aEnd.Row(), i ); + Rectangle aDestRect = pTransClip->GetMMRect( 0, 0, + static_cast<SCCOL>(aClipRange.aEnd.Row() - aClipRange.aStart.Row()), + static_cast<SCROW>(aClipRange.aEnd.Col() - aClipRange.aStart.Col()), i ); + pTransClip->pDrawLayer->CopyFromClip( pDrawLayer, i, aSourceRect, ScAddress(0,0,i), aDestRect ); + } + } + + pTransClip->SetClipParam(GetClipParam()); + pTransClip->GetClipParam().transpose(); + } + else + { + DBG_ERROR("TransposeClip: zu gross"); + } + + // Dies passiert erst beim Einfuegen... + + GetClipParam().mbCutMode = false; +} + +void ScDocument::CopyRangeNamesToClip(ScDocument* pClipDoc, const ScRange& rClipRange, const ScMarkData* pMarks, bool bAllTabs) +{ + std::set<USHORT> aUsedNames; // indexes of named ranges that are used in the copied cells + for (SCTAB i = 0; i <= MAXTAB; ++i) + if (pTab[i] && pClipDoc->pTab[i]) + if ( bAllTabs || !pMarks || pMarks->GetTableSelect(i) ) + pTab[i]->FindRangeNamesInUse( + rClipRange.aStart.Col(), rClipRange.aStart.Row(), + rClipRange.aEnd.Col(), rClipRange.aEnd.Row(), aUsedNames); + + pClipDoc->pRangeName->FreeAll(); + for (USHORT i = 0; i < pRangeName->GetCount(); i++) //! DB-Bereiche Pivot-Bereiche auch !!! + { + USHORT nIndex = ((ScRangeData*)((*pRangeName)[i]))->GetIndex(); + bool bInUse = ( aUsedNames.find(nIndex) != aUsedNames.end() ); + if (bInUse) + { + ScRangeData* pData = new ScRangeData(*((*pRangeName)[i])); + if (!pClipDoc->pRangeName->Insert(pData)) + delete pData; + else + pData->SetIndex(nIndex); + } + } +} + +ScDocument::NumFmtMergeHandler::NumFmtMergeHandler(ScDocument* pDoc, ScDocument* pSrcDoc) : + mpDoc(pDoc) +{ + mpDoc->MergeNumberFormatter(pSrcDoc); +} + +ScDocument::NumFmtMergeHandler::~NumFmtMergeHandler() +{ + mpDoc->pFormatExchangeList = NULL; +} + +void ScDocument::MergeNumberFormatter(ScDocument* pSrcDoc) +{ + SvNumberFormatter* pThisFormatter = xPoolHelper->GetFormTable(); + SvNumberFormatter* pOtherFormatter = pSrcDoc->xPoolHelper->GetFormTable(); + if (pOtherFormatter && pOtherFormatter != pThisFormatter) + { + SvNumberFormatterIndexTable* pExchangeList = + pThisFormatter->MergeFormatter(*(pOtherFormatter)); + if (pExchangeList->Count() > 0) + pFormatExchangeList = pExchangeList; + } +} + +void ScDocument::CopyRangeNamesFromClip(ScDocument* pClipDoc, ScClipRangeNameData& rRangeNames) +{ + sal_uInt16 nClipRangeNameCount = pClipDoc->pRangeName->GetCount(); + ScClipRangeNameData aClipRangeNames; + + // array containing range names which might need update of indices + aClipRangeNames.mpRangeNames.resize(nClipRangeNameCount, NULL); + + for (sal_uInt16 i = 0; i < nClipRangeNameCount; ++i) //! DB-Bereiche Pivot-Bereiche auch + { + /* Copy only if the name doesn't exist in this document. + If it exists we use the already existing name instead, + another possibility could be to create new names if + documents differ. + A proper solution would ask the user how to proceed. + The adjustment of the indices in the formulas is done later. + */ + ScRangeData* pClipRangeData = (*pClipDoc->pRangeName)[i]; + USHORT k; + if ( pRangeName->SearchName( pClipRangeData->GetName(), k ) ) + { + aClipRangeNames.mpRangeNames[i] = NULL; // range name not inserted + USHORT nOldIndex = pClipRangeData->GetIndex(); + USHORT nNewIndex = ((*pRangeName)[k])->GetIndex(); + aClipRangeNames.insert(nOldIndex, nNewIndex); + if ( !aClipRangeNames.mbReplace ) + aClipRangeNames.mbReplace = ( nOldIndex != nNewIndex ); + } + else + { + ScRangeData* pData = new ScRangeData( *pClipRangeData ); + pData->SetDocument(this); + if ( pRangeName->FindIndex( pData->GetIndex() ) ) + pData->SetIndex(0); // need new index, done in Insert + if ( pRangeName->Insert( pData ) ) + { + aClipRangeNames.mpRangeNames[i] = pData; + USHORT nOldIndex = pClipRangeData->GetIndex(); + USHORT nNewIndex = pData->GetIndex(); + aClipRangeNames.insert(nOldIndex, nNewIndex); + if ( !aClipRangeNames.mbReplace ) + aClipRangeNames.mbReplace = ( nOldIndex != nNewIndex ); + } + else + { // must be an overflow + delete pData; + aClipRangeNames.mpRangeNames[i] = NULL; + aClipRangeNames.insert(pClipRangeData->GetIndex(), 0); + aClipRangeNames.mbReplace = true; + } + } + } + rRangeNames = aClipRangeNames; +} + +void ScDocument::UpdateRangeNamesInFormulas( + ScClipRangeNameData& rRangeNames, const ScRangeList& rDestRanges, const ScMarkData& rMark, + SCCOL nXw, SCROW nYw) +{ + // nXw and nYw are the extra width and height of the destination range + // extended due to presence of merged cell(s). + + if (!rRangeNames.mbReplace) + return; + + // first update all inserted named formulas if they contain other + // range names and used indices changed + size_t nRangeNameCount = rRangeNames.mpRangeNames.size(); + for (size_t i = 0; i < nRangeNameCount; ++i) //! DB-Bereiche Pivot-Bereiche auch + { + if ( rRangeNames.mpRangeNames[i] ) + rRangeNames.mpRangeNames[i]->ReplaceRangeNamesInUse(rRangeNames.maRangeMap); + } + // then update the formulas, they might need just the updated range names + for (ULONG nRange = 0; nRange < rDestRanges.Count(); ++nRange) + { + const ScRange* pRange = rDestRanges.GetObject( nRange); + SCCOL nCol1 = pRange->aStart.Col(); + SCROW nRow1 = pRange->aStart.Row(); + SCCOL nCol2 = pRange->aEnd.Col(); + SCROW nRow2 = pRange->aEnd.Row(); + + SCCOL nC1 = nCol1; + SCROW nR1 = nRow1; + SCCOL nC2 = nC1 + nXw; + if (nC2 > nCol2) + nC2 = nCol2; + SCROW nR2 = nR1 + nYw; + if (nR2 > nRow2) + nR2 = nRow2; + do + { + do + { + for (SCTAB k = 0; k <= MAXTAB; k++) + { + if ( pTab[k] && rMark.GetTableSelect(k) ) + pTab[k]->ReplaceRangeNamesInUse(nC1, nR1, + nC2, nR2, rRangeNames.maRangeMap); + } + nC1 = nC2 + 1; + nC2 = Min((SCCOL)(nC1 + nXw), nCol2); + } while (nC1 <= nCol2); + nC1 = nCol1; + nC2 = nC1 + nXw; + if (nC2 > nCol2) + nC2 = nCol2; + nR1 = nR2 + 1; + nR2 = Min((SCROW)(nR1 + nYw), nRow2); + } while (nR1 <= nRow2); + } +} + +ScClipParam& ScDocument::GetClipParam() +{ + if (!mpClipParam.get()) + mpClipParam.reset(new ScClipParam); + + return *mpClipParam; +} + +void ScDocument::SetClipParam(const ScClipParam& rParam) +{ + mpClipParam.reset(new ScClipParam(rParam)); +} + +BOOL ScDocument::IsClipboardSource() const +{ + ScDocument* pClipDoc = SC_MOD()->GetClipDoc(); + return pClipDoc && pClipDoc->xPoolHelper.isValid() && + xPoolHelper->GetDocPool() == pClipDoc->xPoolHelper->GetDocPool(); +} + + +void ScDocument::StartListeningFromClip( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark, USHORT nInsFlag ) +{ + if (nInsFlag & IDF_CONTENTS) + { + for (SCTAB i = 0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->StartListeningInArea( nCol1, nRow1, nCol2, nRow2 ); + } +} + + +void ScDocument::BroadcastFromClip( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark, USHORT nInsFlag ) +{ + if (nInsFlag & IDF_CONTENTS) + { + ScBulkBroadcast aBulkBroadcast( GetBASM()); + for (SCTAB i = 0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->BroadcastInArea( nCol1, nRow1, nCol2, nRow2 ); + } +} + + +void ScDocument::CopyBlockFromClip( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark, + SCsCOL nDx, SCsROW nDy, + const ScCopyBlockFromClipParams* pCBFCP ) +{ + ScTable** ppClipTab = pCBFCP->pClipDoc->pTab; + SCTAB nTabEnd = pCBFCP->nTabEnd; + SCTAB nClipTab = 0; + for (SCTAB i = pCBFCP->nTabStart; i <= nTabEnd; i++) + { + if (pTab[i] && rMark.GetTableSelect(i) ) + { + while (!ppClipTab[nClipTab]) nClipTab = (nClipTab+1) % (MAXTAB+1); + + pTab[i]->CopyFromClip( nCol1, nRow1, nCol2, nRow2, nDx, nDy, + pCBFCP->nInsFlag, pCBFCP->bAsLink, pCBFCP->bSkipAttrForEmpty, ppClipTab[nClipTab] ); + + if ( pCBFCP->pClipDoc->pDrawLayer && ( pCBFCP->nInsFlag & IDF_OBJECTS ) ) + { + // also copy drawing objects + + // drawing layer must be created before calling CopyFromClip + // (ScDocShell::MakeDrawLayer also does InitItems etc.) + DBG_ASSERT( pDrawLayer, "CopyBlockFromClip: No drawing layer" ); + if ( pDrawLayer ) + { + // For GetMMRect, the row heights in the target document must already be valid + // (copied in an extra step before pasting, or updated after pasting cells, but + // before pasting objects). + + Rectangle aSourceRect = pCBFCP->pClipDoc->GetMMRect( + nCol1-nDx, nRow1-nDy, nCol2-nDx, nRow2-nDy, nClipTab ); + Rectangle aDestRect = GetMMRect( nCol1, nRow1, nCol2, nRow2, i ); + pDrawLayer->CopyFromClip( pCBFCP->pClipDoc->pDrawLayer, nClipTab, aSourceRect, + ScAddress( nCol1, nRow1, i ), aDestRect ); + } + } + + nClipTab = (nClipTab+1) % (MAXTAB+1); + } + } + if ( pCBFCP->nInsFlag & IDF_CONTENTS ) + { + nClipTab = 0; + for (SCTAB i = pCBFCP->nTabStart; i <= nTabEnd; i++) + { + if (pTab[i] && rMark.GetTableSelect(i) ) + { + while (!ppClipTab[nClipTab]) nClipTab = (nClipTab+1) % (MAXTAB+1); + SCsTAB nDz = ((SCsTAB)i) - nClipTab; + + // #89081# ranges of consecutive selected tables (in clipboard and dest. doc) + // must be handled in one UpdateReference call + SCTAB nFollow = 0; + while ( i + nFollow < nTabEnd + && rMark.GetTableSelect( i + nFollow + 1 ) + && nClipTab + nFollow < MAXTAB + && ppClipTab[nClipTab + nFollow + 1] ) + ++nFollow; + + if ( pCBFCP->pClipDoc->GetClipParam().mbCutMode ) + { + BOOL bOldInserting = IsInsertingFromOtherDoc(); + SetInsertingFromOtherDoc( TRUE); + UpdateReference( URM_MOVE, + nCol1, nRow1, i, nCol2, nRow2, i+nFollow, + nDx, nDy, nDz, pCBFCP->pRefUndoDoc ); + SetInsertingFromOtherDoc( bOldInserting); + } + else + UpdateReference( URM_COPY, + nCol1, nRow1, i, nCol2, nRow2, i+nFollow, + nDx, nDy, nDz, pCBFCP->pRefUndoDoc, FALSE ); + + nClipTab = (nClipTab+nFollow+1) % (MAXTAB+1); + i = sal::static_int_cast<SCTAB>( i + nFollow ); + } + } + } +} + + +void ScDocument::CopyNonFilteredFromClip( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScMarkData& rMark, + SCsCOL nDx, SCsROW /* nDy */, + const ScCopyBlockFromClipParams* pCBFCP, + SCROW & rClipStartRow ) +{ + // call CopyBlockFromClip for ranges of consecutive non-filtered rows + // nCol1/nRow1 etc. is in target doc + + // filtered state is taken from first used table in clipboard (as in GetClipArea) + SCTAB nFlagTab = 0; + ScTable** ppClipTab = pCBFCP->pClipDoc->pTab; + while ( nFlagTab < MAXTAB && !ppClipTab[nFlagTab] ) + ++nFlagTab; + + const ScBitMaskCompressedArray< SCROW, BYTE> & rSourceFlags = + pCBFCP->pClipDoc->GetRowFlagsArray( nFlagTab); + + SCROW nSourceRow = rClipStartRow; + SCROW nSourceEnd = 0; + if (pCBFCP->pClipDoc->GetClipParam().maRanges.Count()) + nSourceEnd = pCBFCP->pClipDoc->GetClipParam().maRanges.First()->aEnd.Row(); + SCROW nDestRow = nRow1; + + while ( nSourceRow <= nSourceEnd && nDestRow <= nRow2 ) + { + // skip filtered rows + nSourceRow = rSourceFlags.GetFirstForCondition( nSourceRow, nSourceEnd, CR_FILTERED, 0); + + if ( nSourceRow <= nSourceEnd ) + { + // look for more non-filtered rows following + SCROW nFollow = rSourceFlags.GetBitStateEnd( nSourceRow, CR_FILTERED, 0) - nSourceRow; + if (nFollow > nSourceEnd - nSourceRow) + nFollow = nSourceEnd - nSourceRow; + if (nFollow > nRow2 - nDestRow) + nFollow = nRow2 - nDestRow; + + SCsROW nNewDy = ((SCsROW)nDestRow) - nSourceRow; + CopyBlockFromClip( nCol1, nDestRow, nCol2, nDestRow + nFollow, rMark, nDx, nNewDy, pCBFCP ); + + nSourceRow += nFollow + 1; + nDestRow += nFollow + 1; + } + } + rClipStartRow = nSourceRow; +} + + +void ScDocument::CopyFromClip( const ScRange& rDestRange, const ScMarkData& rMark, + USHORT nInsFlag, + ScDocument* pRefUndoDoc, ScDocument* pClipDoc, BOOL bResetCut, + BOOL bAsLink, BOOL bIncludeFiltered, BOOL bSkipAttrForEmpty, + const ScRangeList * pDestRanges ) +{ + if (!bIsClip) + { + if (!pClipDoc) + { + DBG_ERROR("CopyFromClip: no ClipDoc"); + pClipDoc = SC_MOD()->GetClipDoc(); + } + if (pClipDoc->bIsClip && pClipDoc->GetTableCount()) + { + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // avoid multiple recalculations + + NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc); + + ScClipRangeNameData aClipRangeNames; + CopyRangeNamesFromClip(pClipDoc, aClipRangeNames); + + SCCOL nAllCol1 = rDestRange.aStart.Col(); + SCROW nAllRow1 = rDestRange.aStart.Row(); + SCCOL nAllCol2 = rDestRange.aEnd.Col(); + SCROW nAllRow2 = rDestRange.aEnd.Row(); + + SCCOL nXw = 0; + SCROW nYw = 0; + ScRange aClipRange = pClipDoc->GetClipParam().getWholeRange(); + for (SCTAB nTab = 0; nTab <= MAXTAB; nTab++) // find largest merge overlap + if (pClipDoc->pTab[nTab]) // all sheets of the clipboard content + { + SCCOL nThisEndX = aClipRange.aEnd.Col(); + SCROW nThisEndY = aClipRange.aEnd.Row(); + pClipDoc->ExtendMerge( aClipRange.aStart.Col(), + aClipRange.aStart.Row(), + nThisEndX, nThisEndY, nTab ); + // only extra value from ExtendMerge + nThisEndX = sal::static_int_cast<SCCOL>( nThisEndX - aClipRange.aEnd.Col() ); + nThisEndY = sal::static_int_cast<SCROW>( nThisEndY - aClipRange.aEnd.Row() ); + if ( nThisEndX > nXw ) + nXw = nThisEndX; + if ( nThisEndY > nYw ) + nYw = nThisEndY; + } + + SCCOL nDestAddX; + SCROW nDestAddY; + pClipDoc->GetClipArea( nDestAddX, nDestAddY, bIncludeFiltered ); + nXw = sal::static_int_cast<SCCOL>( nXw + nDestAddX ); + nYw = sal::static_int_cast<SCROW>( nYw + nDestAddY ); // ClipArea, plus ExtendMerge value + + /* Decide which contents to delete before copying. Delete all + contents if nInsFlag contains any real content flag. + #i102056# Notes are pasted from clipboard in a second pass, + together with the special flag IDF_ADDNOTES that states to not + overwrite/delete existing cells but to insert the notes into + these cells. In this case, just delete old notes from the + destination area. */ + USHORT nDelFlag = IDF_NONE; + if ( (nInsFlag & (IDF_CONTENTS | IDF_ADDNOTES)) == (IDF_NOTE | IDF_ADDNOTES) ) + nDelFlag |= IDF_NOTE; + else if ( nInsFlag & IDF_CONTENTS ) + nDelFlag |= IDF_CONTENTS; + // With bSkipAttrForEmpty, don't remove attributes, copy + // on top of existing attributes instead. + if ( ( nInsFlag & IDF_ATTRIB ) && !bSkipAttrForEmpty ) + nDelFlag |= IDF_ATTRIB; + + ScCopyBlockFromClipParams aCBFCP; + aCBFCP.pRefUndoDoc = pRefUndoDoc; + aCBFCP.pClipDoc = pClipDoc; + aCBFCP.nInsFlag = nInsFlag; + aCBFCP.bAsLink = bAsLink; + aCBFCP.bSkipAttrForEmpty = bSkipAttrForEmpty; + aCBFCP.nTabStart = MAXTAB; // wird in der Schleife angepasst + aCBFCP.nTabEnd = 0; // wird in der Schleife angepasst + + // Inc/DecRecalcLevel einmal aussen, damit nicht fuer jeden Block + // die Draw-Seitengroesse neu berechnet werden muss + //! nur wenn ganze Zeilen/Spalten kopiert werden? + + for (SCTAB j = 0; j <= MAXTAB; j++) + if (pTab[j] && rMark.GetTableSelect(j)) + { + if ( j < aCBFCP.nTabStart ) + aCBFCP.nTabStart = j; + aCBFCP.nTabEnd = j; + pTab[j]->IncRecalcLevel(); + } + + ScRangeList aLocalRangeList; + if (!pDestRanges) + { + aLocalRangeList.Append( rDestRange); + pDestRanges = &aLocalRangeList; + } + + bInsertingFromOtherDoc = TRUE; // kein Broadcast/Listener aufbauen bei Insert + + // bei mindestens 64 Zeilen wird in ScColumn::CopyFromClip voralloziert + BOOL bDoDouble = ( nYw < 64 && nAllRow2 - nAllRow1 > 64); + BOOL bOldDouble = ScColumn::bDoubleAlloc; + if (bDoDouble) + ScColumn::bDoubleAlloc = TRUE; + + SCCOL nClipStartCol = aClipRange.aStart.Col(); + SCROW nClipStartRow = aClipRange.aStart.Row(); + // WaE: commented because unused: SCCOL nClipEndCol = pClipDoc->aClipRange.aEnd.Col(); + SCROW nClipEndRow = aClipRange.aEnd.Row(); + for (ULONG nRange = 0; nRange < pDestRanges->Count(); ++nRange) + { + const ScRange* pRange = pDestRanges->GetObject( nRange); + SCCOL nCol1 = pRange->aStart.Col(); + SCROW nRow1 = pRange->aStart.Row(); + SCCOL nCol2 = pRange->aEnd.Col(); + SCROW nRow2 = pRange->aEnd.Row(); + + DeleteArea(nCol1, nRow1, nCol2, nRow2, rMark, nDelFlag); + + SCCOL nC1 = nCol1; + SCROW nR1 = nRow1; + SCCOL nC2 = nC1 + nXw; + if (nC2 > nCol2) + nC2 = nCol2; + SCROW nR2 = nR1 + nYw; + if (nR2 > nRow2) + nR2 = nRow2; + + do + { + // Pasting is done column-wise, when pasting to a filtered + // area this results in partitioning and we have to + // remember and reset the start row for each column until + // it can be advanced for the next chunk of unfiltered + // rows. + SCROW nSaveClipStartRow = nClipStartRow; + do + { + nClipStartRow = nSaveClipStartRow; + SCsCOL nDx = ((SCsCOL)nC1) - nClipStartCol; + SCsROW nDy = ((SCsROW)nR1) - nClipStartRow; + if ( bIncludeFiltered ) + { + CopyBlockFromClip( nC1, nR1, nC2, nR2, rMark, nDx, + nDy, &aCBFCP ); + nClipStartRow += nR2 - nR1 + 1; + } + else + { + CopyNonFilteredFromClip( nC1, nR1, nC2, nR2, rMark, + nDx, nDy, &aCBFCP, nClipStartRow ); + } + // Not needed for columns, but if it was this would be how to. + //if (nClipStartCol > nClipEndCol) + // nClipStartCol = pClipDoc->aClipRange.aStart.Col(); + nC1 = nC2 + 1; + nC2 = Min((SCCOL)(nC1 + nXw), nCol2); + } while (nC1 <= nCol2); + if (nClipStartRow > nClipEndRow) + nClipStartRow = aClipRange.aStart.Row(); + nC1 = nCol1; + nC2 = nC1 + nXw; + if (nC2 > nCol2) + nC2 = nCol2; + nR1 = nR2 + 1; + nR2 = Min((SCROW)(nR1 + nYw), nRow2); + } while (nR1 <= nRow2); + } + + ScColumn::bDoubleAlloc = bOldDouble; + + for (SCTAB k = 0; k <= MAXTAB; k++) + if (pTab[k] && rMark.GetTableSelect(k)) + pTab[k]->DecRecalcLevel(); + + bInsertingFromOtherDoc = FALSE; + + UpdateRangeNamesInFormulas(aClipRangeNames, *pDestRanges, rMark, nXw, nYw); + + // Listener aufbauen nachdem alles inserted wurde + StartListeningFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag ); + // nachdem alle Listener aufgebaut wurden, kann gebroadcastet werden + BroadcastFromClip( nAllCol1, nAllRow1, nAllCol2, nAllRow2, rMark, nInsFlag ); + if (bResetCut) + pClipDoc->GetClipParam().mbCutMode = false; + SetAutoCalc( bOldAutoCalc ); + } + } +} + +static SCROW lcl_getLastNonFilteredRow( + const ScBitMaskCompressedArray<SCROW, BYTE>& rFlags, SCROW nBegRow, SCROW nEndRow, + SCROW nRowCount) +{ + SCROW nFilteredRow = rFlags.GetFirstForCondition( + nBegRow, nEndRow, CR_FILTERED, CR_FILTERED); + + SCROW nRow = nFilteredRow - 1; + if (nRow - nBegRow + 1 > nRowCount) + // make sure the row range stays within the data size. + nRow = nBegRow + nRowCount - 1; + + return nRow; +} + +void ScDocument::CopyMultiRangeFromClip( + const ScAddress& rDestPos, const ScMarkData& rMark, sal_uInt16 nInsFlag, ScDocument* pClipDoc, + bool bResetCut, bool bAsLink, bool /*bIncludeFiltered*/, bool bSkipAttrForEmpty) +{ + if (bIsClip) + return; + + if (!pClipDoc->bIsClip || !pClipDoc->GetTableCount()) + // There is nothing in the clip doc to copy. + return; + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // avoid multiple recalculations + + NumFmtMergeHandler aNumFmtMergeHdl(this, pClipDoc); + + ScClipRangeNameData aClipRangeNames; + CopyRangeNamesFromClip(pClipDoc, aClipRangeNames); + + SCCOL nCol1 = rDestPos.Col(); + SCROW nRow1 = rDestPos.Row(); + ScClipParam& rClipParam = pClipDoc->GetClipParam(); + + ScCopyBlockFromClipParams aCBFCP; + aCBFCP.pRefUndoDoc = NULL; + aCBFCP.pClipDoc = pClipDoc; + aCBFCP.nInsFlag = nInsFlag; + aCBFCP.bAsLink = bAsLink; + aCBFCP.bSkipAttrForEmpty = bSkipAttrForEmpty; + aCBFCP.nTabStart = MAXTAB; + aCBFCP.nTabEnd = 0; + + for (SCTAB j = 0; j <= MAXTAB; ++j) + { + if (pTab[j] && rMark.GetTableSelect(j)) + { + if ( j < aCBFCP.nTabStart ) + aCBFCP.nTabStart = j; + aCBFCP.nTabEnd = j; + pTab[j]->IncRecalcLevel(); + } + } + + ScRange aDestRange; + rMark.GetMarkArea(aDestRange); + SCROW nLastMarkedRow = aDestRange.aEnd.Row(); + + bInsertingFromOtherDoc = TRUE; // kein Broadcast/Listener aufbauen bei Insert + + SCROW nBegRow = nRow1; + sal_uInt16 nDelFlag = IDF_CONTENTS; + const ScBitMaskCompressedArray<SCROW, BYTE>& rFlags = GetRowFlagsArray(aCBFCP.nTabStart); + + for (ScRange* p = rClipParam.maRanges.First(); p; p = rClipParam.maRanges.Next()) + { + // The begin row must not be filtered. + + SCROW nRowCount = p->aEnd.Row() - p->aStart.Row() + 1; + + SCsCOL nDx = static_cast<SCsCOL>(nCol1 - p->aStart.Col()); + SCsROW nDy = static_cast<SCsROW>(nBegRow - p->aStart.Row()); + SCCOL nCol2 = nCol1 + p->aEnd.Col() - p->aStart.Col(); + + SCROW nEndRow = lcl_getLastNonFilteredRow(rFlags, nBegRow, nLastMarkedRow, nRowCount); + + if (!bSkipAttrForEmpty) + DeleteArea(nCol1, nBegRow, nCol2, nEndRow, rMark, nDelFlag); + + CopyBlockFromClip(nCol1, nBegRow, nCol2, nEndRow, rMark, nDx, nDy, &aCBFCP); + nRowCount -= nEndRow - nBegRow + 1; + + while (nRowCount > 0) + { + // Get the first non-filtered row. + SCROW nNonFilteredRow = rFlags.GetFirstForCondition(nEndRow+1, nLastMarkedRow, CR_FILTERED, 0); + if (nNonFilteredRow > nLastMarkedRow) + return; + + SCROW nRowsSkipped = nNonFilteredRow - nEndRow - 1; + nDy += nRowsSkipped; + + nBegRow = nNonFilteredRow; + nEndRow = lcl_getLastNonFilteredRow(rFlags, nBegRow, nLastMarkedRow, nRowCount); + + if (!bSkipAttrForEmpty) + DeleteArea(nCol1, nBegRow, nCol2, nEndRow, rMark, nDelFlag); + + CopyBlockFromClip(nCol1, nBegRow, nCol2, nEndRow, rMark, nDx, nDy, &aCBFCP); + nRowCount -= nEndRow - nBegRow + 1; + } + + if (rClipParam.meDirection == ScClipParam::Row) + // Begin row for the next range being pasted. + nBegRow = rFlags.GetFirstForCondition(nEndRow+1, nLastMarkedRow, CR_FILTERED, 0); + else + nBegRow = nRow1; + + if (rClipParam.meDirection == ScClipParam::Column) + nCol1 += p->aEnd.Col() - p->aStart.Col() + 1; + } + + for (SCTAB i = 0; i <= MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->DecRecalcLevel(); + + bInsertingFromOtherDoc = FALSE; + + ScRangeList aRanges; + aRanges.Append(aDestRange); + SCCOL nCols = aDestRange.aEnd.Col() - aDestRange.aStart.Col() + 1; + SCROW nRows = aDestRange.aEnd.Row() - aDestRange.aStart.Row() + 1; + UpdateRangeNamesInFormulas(aClipRangeNames, aRanges, rMark, nCols-1, nRows-1); + + // Listener aufbauen nachdem alles inserted wurde + StartListeningFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(), + aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag ); + // nachdem alle Listener aufgebaut wurden, kann gebroadcastet werden + BroadcastFromClip(aDestRange.aStart.Col(), aDestRange.aStart.Row(), + aDestRange.aEnd.Col(), aDestRange.aEnd.Row(), rMark, nInsFlag ); + + if (bResetCut) + pClipDoc->GetClipParam().mbCutMode = false; + SetAutoCalc( bOldAutoCalc ); +} + +void ScDocument::SetClipArea( const ScRange& rArea, BOOL bCut ) +{ + if (bIsClip) + { + ScClipParam& rClipParam = GetClipParam(); + rClipParam.maRanges.RemoveAll(); + rClipParam.maRanges.Append(rArea); + rClipParam.mbCutMode = bCut; + } + else + { + DBG_ERROR("SetClipArea: kein Clip"); + } +} + + +void ScDocument::GetClipArea(SCCOL& nClipX, SCROW& nClipY, BOOL bIncludeFiltered) +{ + if (!bIsClip) + { + DBG_ERROR("GetClipArea: kein Clip"); + return; + } + + ScRangeList& rClipRanges = GetClipParam().maRanges; + if (!rClipRanges.Count()) + // No clip range. Bail out. + return; + + ScRangePtr p = rClipRanges.First(); + SCCOL nStartCol = p->aStart.Col(); + SCCOL nEndCol = p->aEnd.Col(); + SCROW nStartRow = p->aStart.Row(); + SCROW nEndRow = p->aEnd.Row(); + for (p = rClipRanges.Next(); p; p = rClipRanges.Next()) + { + if (p->aStart.Col() < nStartCol) + nStartCol = p->aStart.Col(); + if (p->aStart.Row() < nStartRow) + nStartRow = p->aStart.Row(); + if (p->aEnd.Col() > nEndCol) + nEndCol = p->aEnd.Col(); + if (p->aEnd.Row() < nEndRow) + nEndRow = p->aEnd.Row(); + } + + nClipX = nEndCol - nStartCol; + + if ( bIncludeFiltered ) + nClipY = nEndRow - nStartRow; + else + { + // count non-filtered rows + // count on first used table in clipboard + SCTAB nCountTab = 0; + while ( nCountTab < MAXTAB && !pTab[nCountTab] ) + ++nCountTab; + + SCROW nResult = GetRowFlagsArray( nCountTab).CountForCondition( + nStartRow, nEndRow, CR_FILTERED, 0); + + if ( nResult > 0 ) + nClipY = nResult - 1; + else + nClipY = 0; // always return at least 1 row + } +} + + +void ScDocument::GetClipStart(SCCOL& nClipX, SCROW& nClipY) +{ + if (bIsClip) + { + ScRangeList& rClipRanges = GetClipParam().maRanges; + if (rClipRanges.Count()) + { + nClipX = rClipRanges.First()->aStart.Col(); + nClipY = rClipRanges.First()->aStart.Row(); + } + } + else + { + DBG_ERROR("GetClipStart: kein Clip"); + } +} + + +BOOL ScDocument::HasClipFilteredRows() +{ + // count on first used table in clipboard + SCTAB nCountTab = 0; + while ( nCountTab < MAXTAB && !pTab[nCountTab] ) + ++nCountTab; + + ScRangeList& rClipRanges = GetClipParam().maRanges; + if (!rClipRanges.Count()) + return false; + + return GetRowFlagsArray( nCountTab).HasCondition( rClipRanges.First()->aStart.Row(), + rClipRanges.First()->aEnd.Row(), CR_FILTERED, CR_FILTERED); +} + + +void ScDocument::MixDocument( const ScRange& rRange, USHORT nFunction, BOOL bSkipEmpty, + ScDocument* pSrcDoc ) +{ + SCTAB nTab1 = rRange.aStart.Tab(); + SCTAB nTab2 = rRange.aEnd.Tab(); + for (SCTAB i = nTab1; i <= nTab2; i++) + if (pTab[i] && pSrcDoc->pTab[i]) + pTab[i]->MixData( rRange.aStart.Col(), rRange.aStart.Row(), + rRange.aEnd.Col(), rRange.aEnd.Row(), + nFunction, bSkipEmpty, pSrcDoc->pTab[i] ); +} + + +void ScDocument::FillTab( const ScRange& rSrcArea, const ScMarkData& rMark, + USHORT nFlags, USHORT nFunction, + BOOL bSkipEmpty, BOOL bAsLink ) +{ + USHORT nDelFlags = nFlags; + if (nDelFlags & IDF_CONTENTS) + nDelFlags |= IDF_CONTENTS; // immer alle Inhalte oder keine loeschen! + + SCTAB nSrcTab = rSrcArea.aStart.Tab(); + + if (ValidTab(nSrcTab) && pTab[nSrcTab]) + { + SCCOL nStartCol = rSrcArea.aStart.Col(); + SCROW nStartRow = rSrcArea.aStart.Row(); + SCCOL nEndCol = rSrcArea.aEnd.Col(); + SCROW nEndRow = rSrcArea.aEnd.Row(); + ScDocument* pMixDoc = NULL; + BOOL bDoMix = ( bSkipEmpty || nFunction ) && ( nFlags & IDF_CONTENTS ); + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + SCTAB nCount = GetTableCount(); + for (SCTAB i=0; i<nCount; i++) + if ( i!=nSrcTab && pTab[i] && rMark.GetTableSelect(i) ) + { + if (bDoMix) + { + if (!pMixDoc) + { + pMixDoc = new ScDocument( SCDOCMODE_UNDO ); + pMixDoc->InitUndo( this, i, i ); + } + else + pMixDoc->AddUndoTab( i, i ); + pTab[i]->CopyToTable( nStartCol,nStartRow, nEndCol,nEndRow, + IDF_CONTENTS, FALSE, pMixDoc->pTab[i] ); + } + pTab[i]->DeleteArea( nStartCol,nStartRow, nEndCol,nEndRow, nDelFlags); + pTab[nSrcTab]->CopyToTable( nStartCol,nStartRow, nEndCol,nEndRow, + nFlags, FALSE, pTab[i], NULL, bAsLink ); + + if (bDoMix) + pTab[i]->MixData( nStartCol,nStartRow, nEndCol,nEndRow, + nFunction, bSkipEmpty, pMixDoc->pTab[i] ); + } + + delete pMixDoc; + + SetAutoCalc( bOldAutoCalc ); + } + else + { + DBG_ERROR("falsche Tabelle"); + } +} + + +void ScDocument::FillTabMarked( SCTAB nSrcTab, const ScMarkData& rMark, + USHORT nFlags, USHORT nFunction, + BOOL bSkipEmpty, BOOL bAsLink ) +{ + USHORT nDelFlags = nFlags; + if (nDelFlags & IDF_CONTENTS) + nDelFlags |= IDF_CONTENTS; // immer alle Inhalte oder keine loeschen! + + if (ValidTab(nSrcTab) && pTab[nSrcTab]) + { + ScDocument* pMixDoc = NULL; + BOOL bDoMix = ( bSkipEmpty || nFunction ) && ( nFlags & IDF_CONTENTS ); + + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + + ScRange aArea; + rMark.GetMultiMarkArea( aArea ); + SCCOL nStartCol = aArea.aStart.Col(); + SCROW nStartRow = aArea.aStart.Row(); + SCCOL nEndCol = aArea.aEnd.Col(); + SCROW nEndRow = aArea.aEnd.Row(); + + SCTAB nCount = GetTableCount(); + for (SCTAB i=0; i<nCount; i++) + if ( i!=nSrcTab && pTab[i] && rMark.GetTableSelect(i) ) + { + if (bDoMix) + { + if (!pMixDoc) + { + pMixDoc = new ScDocument( SCDOCMODE_UNDO ); + pMixDoc->InitUndo( this, i, i ); + } + else + pMixDoc->AddUndoTab( i, i ); + pTab[i]->CopyToTable( nStartCol,nStartRow, nEndCol,nEndRow, + IDF_CONTENTS, TRUE, pMixDoc->pTab[i], &rMark ); + } + + pTab[i]->DeleteSelection( nDelFlags, rMark ); + pTab[nSrcTab]->CopyToTable( nStartCol,nStartRow, nEndCol,nEndRow, + nFlags, TRUE, pTab[i], &rMark, bAsLink ); + + if (bDoMix) + pTab[i]->MixMarked( rMark, nFunction, bSkipEmpty, pMixDoc->pTab[i] ); + } + + delete pMixDoc; + + SetAutoCalc( bOldAutoCalc ); + } + else + { + DBG_ERROR("falsche Tabelle"); + } +} + + +void ScDocument::PutCell( SCCOL nCol, SCROW nRow, SCTAB nTab, ScBaseCell* pCell, BOOL bForceTab ) +{ + if (VALIDTAB(nTab)) + { + if ( bForceTab && !pTab[nTab] ) + { + BOOL bExtras = !bIsUndo; // Spaltenbreiten, Zeilenhoehen, Flags + + pTab[nTab] = new ScTable(this, nTab, + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("temp")), + bExtras, bExtras); + ++nMaxTableNumber; + } + + if (pTab[nTab]) + pTab[nTab]->PutCell( nCol, nRow, pCell ); + } +} + + +void ScDocument::PutCell( const ScAddress& rPos, ScBaseCell* pCell, BOOL bForceTab ) +{ + SCTAB nTab = rPos.Tab(); + if ( bForceTab && !pTab[nTab] ) + { + BOOL bExtras = !bIsUndo; // Spaltenbreiten, Zeilenhoehen, Flags + + pTab[nTab] = new ScTable(this, nTab, + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("temp")), + bExtras, bExtras); + ++nMaxTableNumber; + } + + if (pTab[nTab]) + pTab[nTab]->PutCell( rPos, pCell ); +} + + +BOOL ScDocument::SetString( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rString ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->SetString( nCol, nRow, nTab, rString ); + else + return FALSE; +} + + +void ScDocument::SetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, const double& rVal ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->SetValue( nCol, nRow, rVal ); +} + + +void ScDocument::GetString( SCCOL nCol, SCROW nRow, SCTAB nTab, String& rString ) +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + pTab[nTab]->GetString( nCol, nRow, rString ); + else + rString.Erase(); +} + + +void ScDocument::GetInputString( SCCOL nCol, SCROW nRow, SCTAB nTab, String& rString ) +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + pTab[nTab]->GetInputString( nCol, nRow, rString ); + else + rString.Erase(); +} + + +void ScDocument::GetValue( SCCOL nCol, SCROW nRow, SCTAB nTab, double& rValue ) +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + rValue = pTab[nTab]->GetValue( nCol, nRow ); + else + rValue = 0.0; +} + + +double ScDocument::GetValue( const ScAddress& rPos ) +{ + SCTAB nTab = rPos.Tab(); + if ( pTab[nTab] ) + return pTab[nTab]->GetValue( rPos ); + return 0.0; +} + + +void ScDocument::GetNumberFormat( SCCOL nCol, SCROW nRow, SCTAB nTab, + sal_uInt32& rFormat ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + { + rFormat = pTab[nTab]->GetNumberFormat( nCol, nRow ); + return ; + } + rFormat = 0; +} + + +sal_uInt32 ScDocument::GetNumberFormat( const ScAddress& rPos ) const +{ + SCTAB nTab = rPos.Tab(); + if ( pTab[nTab] ) + return pTab[nTab]->GetNumberFormat( rPos ); + return 0; +} + + +void ScDocument::GetNumberFormatInfo( short& nType, ULONG& nIndex, + const ScAddress& rPos, const ScBaseCell* pCell ) const +{ + SCTAB nTab = rPos.Tab(); + if ( pTab[nTab] ) + { + nIndex = pTab[nTab]->GetNumberFormat( rPos ); + if ( (nIndex % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && pCell && + pCell->GetCellType() == CELLTYPE_FORMULA ) + static_cast<const ScFormulaCell*>(pCell)->GetFormatInfo( nType, nIndex ); + else + nType = GetFormatTable()->GetType( nIndex ); + } + else + { + nType = NUMBERFORMAT_UNDEFINED; + nIndex = 0; + } +} + + +void ScDocument::GetFormula( SCCOL nCol, SCROW nRow, SCTAB nTab, String& rFormula, + BOOL bAsciiExport ) const +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + pTab[nTab]->GetFormula( nCol, nRow, rFormula, bAsciiExport ); + else + rFormula.Erase(); +} + + +CellType ScDocument::GetCellType( const ScAddress& rPos ) const +{ + SCTAB nTab = rPos.Tab(); + if ( pTab[nTab] ) + return pTab[nTab]->GetCellType( rPos ); + return CELLTYPE_NONE; +} + + +void ScDocument::GetCellType( SCCOL nCol, SCROW nRow, SCTAB nTab, + CellType& rCellType ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + rCellType = pTab[nTab]->GetCellType( nCol, nRow ); + else + rCellType = CELLTYPE_NONE; +} + + +void ScDocument::GetCell( SCCOL nCol, SCROW nRow, SCTAB nTab, + ScBaseCell*& rpCell ) const +{ + if (ValidTab(nTab) && pTab[nTab]) + rpCell = pTab[nTab]->GetCell( nCol, nRow ); + else + { + DBG_ERROR("GetCell ohne Tabelle"); + rpCell = NULL; + } +} + + +ScBaseCell* ScDocument::GetCell( const ScAddress& rPos ) const +{ + SCTAB nTab = rPos.Tab(); + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetCell( rPos ); + + DBG_ERROR("GetCell ohne Tabelle"); + return NULL; +} + + +BOOL ScDocument::HasStringData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + return pTab[nTab]->HasStringData( nCol, nRow ); + else + return FALSE; +} + + +BOOL ScDocument::HasValueData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + return pTab[nTab]->HasValueData( nCol, nRow ); + else + return FALSE; +} + + +BOOL ScDocument::HasStringCells( const ScRange& rRange ) const +{ + // TRUE, wenn String- oder Editzellen im Bereich + + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCTAB nStartTab = rRange.aStart.Tab(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + SCTAB nEndTab = rRange.aEnd.Tab(); + + for ( SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++ ) + if ( pTab[nTab] && pTab[nTab]->HasStringCells( nStartCol, nStartRow, nEndCol, nEndRow ) ) + return TRUE; + + return FALSE; +} + + +BOOL ScDocument::HasSelectionData( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + sal_uInt32 nValidation = static_cast< const SfxUInt32Item* >( GetAttr( nCol, nRow, nTab, ATTR_VALIDDATA ) )->GetValue(); + if( nValidation ) + { + const ScValidationData* pData = GetValidationEntry( nValidation ); + if( pData && pData->HasSelectionList() ) + return TRUE; + } + return HasStringCells( ScRange( nCol, 0, nTab, nCol, MAXROW, nTab ) ); +} + + +ScPostIt* ScDocument::GetNote( const ScAddress& rPos ) +{ + ScTable* pTable = ValidTab( rPos.Tab() ) ? pTab[ rPos.Tab() ] : 0; + return pTable ? pTable->GetNote( rPos.Col(), rPos.Row() ) : 0; +} + + +void ScDocument::TakeNote( const ScAddress& rPos, ScPostIt*& rpNote ) +{ + if( ValidTab( rPos.Tab() ) && pTab[ rPos.Tab() ] ) + pTab[ rPos.Tab() ]->TakeNote( rPos.Col(), rPos.Row(), rpNote ); + else + DELETEZ( rpNote ); +} + + +ScPostIt* ScDocument::ReleaseNote( const ScAddress& rPos ) +{ + ScTable* pTable = ValidTab( rPos.Tab() ) ? pTab[ rPos.Tab() ] : 0; + return pTable ? pTable->ReleaseNote( rPos.Col(), rPos.Row() ) : 0; +} + + +ScPostIt* ScDocument::GetOrCreateNote( const ScAddress& rPos ) +{ + ScPostIt* pNote = GetNote( rPos ); + if( !pNote ) + { + pNote = new ScPostIt( *this, rPos, false ); + TakeNote( rPos, pNote ); + } + return pNote; +} + + +void ScDocument::DeleteNote( const ScAddress& rPos ) +{ + if( ValidTab( rPos.Tab() ) && pTab[ rPos.Tab() ] ) + pTab[ rPos.Tab() ]->DeleteNote( rPos.Col(), rPos.Row() ); +} + + +void ScDocument::InitializeNoteCaptions( SCTAB nTab, bool bForced ) +{ + if( ValidTab( nTab ) && pTab[ nTab ] ) + pTab[ nTab ]->InitializeNoteCaptions( bForced ); +} + +void ScDocument::InitializeAllNoteCaptions( bool bForced ) +{ + for( SCTAB nTab = 0; nTab < GetTableCount(); ++nTab ) + InitializeNoteCaptions( nTab, bForced ); +} + +void ScDocument::SetDirty() +{ + BOOL bOldAutoCalc = GetAutoCalc(); + bAutoCalc = FALSE; // keine Mehrfachberechnung + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( GetBASM()); + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->SetDirty(); + } + + // Charts werden zwar auch ohne AutoCalc im Tracking auf Dirty gesetzt, + // wenn alle Formeln dirty sind, werden die Charts aber nicht mehr erwischt + // (#45205#) - darum alle Charts nochmal explizit + if (pChartListenerCollection) + pChartListenerCollection->SetDirty(); + + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::SetDirty( const ScRange& rRange ) +{ + BOOL bOldAutoCalc = GetAutoCalc(); + bAutoCalc = FALSE; // keine Mehrfachberechnung + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( GetBASM()); + SCTAB nTab2 = rRange.aEnd.Tab(); + for (SCTAB i=rRange.aStart.Tab(); i<=nTab2; i++) + if (pTab[i]) pTab[i]->SetDirty( rRange ); + } + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::SetTableOpDirty( const ScRange& rRange ) +{ + BOOL bOldAutoCalc = GetAutoCalc(); + bAutoCalc = FALSE; // no multiple recalculation + SCTAB nTab2 = rRange.aEnd.Tab(); + for (SCTAB i=rRange.aStart.Tab(); i<=nTab2; i++) + if (pTab[i]) pTab[i]->SetTableOpDirty( rRange ); + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::InterpretDirtyCells( const ScRangeList& rRanges ) +{ + ULONG nRangeCount = rRanges.Count(); + for (ULONG nPos=0; nPos<nRangeCount; nPos++) + { + ScCellIterator aIter( this, *rRanges.GetObject(nPos) ); + ScBaseCell* pCell = aIter.GetFirst(); + while (pCell) + { + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + if ( static_cast<ScFormulaCell*>(pCell)->GetDirty() && GetAutoCalc() ) + static_cast<ScFormulaCell*>(pCell)->Interpret(); + } + pCell = aIter.GetNext(); + } + } +} + + +void ScDocument::AddTableOpFormulaCell( ScFormulaCell* pCell ) +{ + ScInterpreterTableOpParams* p = aTableOpList.Last(); + if ( p && p->bCollectNotifications ) + { + if ( p->bRefresh ) + { // refresh pointers only + p->aNotifiedFormulaCells.push_back( pCell ); + } + else + { // init both, address and pointer + p->aNotifiedFormulaCells.push_back( pCell ); + p->aNotifiedFormulaPos.push_back( pCell->aPos ); + } + } +} + + +void ScDocument::CalcAll() +{ + ClearLookupCaches(); // Ensure we don't deliver zombie data. + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( TRUE ); + SCTAB i; + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->SetDirtyVar(); + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->CalcAll(); + ClearFormulaTree(); + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::CompileAll() +{ + if ( pCondFormList ) + pCondFormList->CompileAll(); + + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->CompileAll(); + SetDirty(); +} + + +void ScDocument::CompileXML() +{ + BOOL bOldAutoCalc = GetAutoCalc(); + SetAutoCalc( FALSE ); + ScProgress aProgress( GetDocumentShell(), ScGlobal::GetRscString( + STR_PROGRESS_CALCULATING ), GetXMLImportedFormulaCount() ); + + // #b6355215# set AutoNameCache to speed up automatic name lookup + DBG_ASSERT( !pAutoNameCache, "AutoNameCache already set" ); + pAutoNameCache = new ScAutoNameCache( this ); + + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->CompileXML( aProgress ); + + DELETEZ( pAutoNameCache ); // valid only during CompileXML, where cell contents don't change + + if ( pCondFormList ) + pCondFormList->CompileXML(); + if ( pValidationList ) + pValidationList->CompileXML(); + + SetDirty(); + SetAutoCalc( bOldAutoCalc ); +} + + +void ScDocument::CalcAfterLoad() +{ + SCTAB i; + + if (bIsClip) // Excel-Dateien werden aus dem Clipboard in ein Clip-Doc geladen + return; // dann wird erst beim Einfuegen in das richtige Doc berechnet + + bCalcingAfterLoad = TRUE; + for ( i = 0; i <= MAXTAB; i++) + if (pTab[i]) pTab[i]->CalcAfterLoad(); + for (i=0; i<=MAXTAB; i++) + if (pTab[i]) pTab[i]->SetDirtyAfterLoad(); + bCalcingAfterLoad = FALSE; + + SetDetectiveDirty(FALSE); // noch keine wirklichen Aenderungen +} + + +USHORT ScDocument::GetErrCode( const ScAddress& rPos ) const +{ + SCTAB nTab = rPos.Tab(); + if ( pTab[nTab] ) + return pTab[nTab]->GetErrCode( rPos ); + return 0; +} + + +void ScDocument::ResetChanged( const ScRange& rRange ) +{ + SCTAB nStartTab = rRange.aStart.Tab(); + SCTAB nEndTab = rRange.aEnd.Tab(); + for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) + if (pTab[nTab]) + pTab[nTab]->ResetChanged( rRange ); +} + +// +// Spaltenbreiten / Zeilenhoehen -------------------------------------- +// + + +void ScDocument::SetColWidth( SCCOL nCol, SCTAB nTab, USHORT nNewWidth ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetColWidth( nCol, nNewWidth ); +} + + +void ScDocument::SetRowHeight( SCROW nRow, SCTAB nTab, USHORT nNewHeight ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetRowHeight( nRow, nNewHeight ); +} + + +void ScDocument::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, USHORT nNewHeight ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetRowHeightRange + ( nStartRow, nEndRow, nNewHeight, 1.0, 1.0 ); +} + + +void ScDocument::SetManualHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, BOOL bManual ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetManualHeight( nStartRow, nEndRow, bManual ); +} + + +USHORT ScDocument::GetColWidth( SCCOL nCol, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetColWidth( nCol ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +USHORT ScDocument::GetOriginalWidth( SCCOL nCol, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetOriginalWidth( nCol ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +USHORT ScDocument::GetCommonWidth( SCCOL nEndCol, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetCommonWidth( nEndCol ); + DBG_ERROR("Wrong table number"); + return 0; +} + + +USHORT ScDocument::GetOriginalHeight( SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetOriginalHeight( nRow ); + DBG_ERROR("Wrong table number"); + return 0; +} + + +USHORT ScDocument::GetRowHeight( SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetRowHeight( nRow ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +ULONG ScDocument::GetRowHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const +{ + if (nStartRow == nEndRow) + return GetRowHeight( nStartRow, nTab); // faster for a single row + + // check bounds because this method replaces former for(i=start;i<=end;++i) loops + if (nStartRow > nEndRow) + return 0; + + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetRowHeight( nStartRow, nEndRow); + + DBG_ERROR("wrong sheet number"); + return 0; +} + +ULONG ScDocument::FastGetRowHeight( SCROW nStartRow, SCROW nEndRow, + SCTAB nTab ) const +{ + return pTab[nTab]->pRowFlags->SumCoupledArrayForCondition( nStartRow, + nEndRow, CR_HIDDEN, 0, *(pTab[nTab]->pRowHeight)); +} + +ULONG ScDocument::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, + SCTAB nTab, double fScale ) const +{ + // faster for a single row + if (nStartRow == nEndRow) + return (ULONG) (GetRowHeight( nStartRow, nTab) * fScale); + + // check bounds because this method replaces former for(i=start;i<=end;++i) loops + if (nStartRow > nEndRow) + return 0; + + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetScaledRowHeight( nStartRow, nEndRow, fScale); + + DBG_ERROR("wrong sheet number"); + return 0; +} + + +const ScSummableCompressedArray< SCROW, USHORT> & ScDocument::GetRowHeightArray( + SCTAB nTab ) const +{ + const ScSummableCompressedArray< SCROW, USHORT> * pHeight; + if ( ValidTab(nTab) && pTab[nTab] ) + pHeight = pTab[nTab]->GetRowHeightArray(); + else + { + DBG_ERROR("wrong sheet number"); + pHeight = 0; + } + if (!pHeight) + { + DBG_ERROR("no row heights at sheet"); + static ScSummableCompressedArray< SCROW, USHORT> aDummy( MAXROW, + ScGlobal::nStdRowHeight); + pHeight = &aDummy; + } + return *pHeight; +} + + +SCROW ScDocument::GetHiddenRowCount( SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetHiddenRowCount( nRow ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +ULONG ScDocument::GetColOffset( SCCOL nCol, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetColOffset( nCol ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +ULONG ScDocument::GetRowOffset( SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetRowOffset( nRow ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +USHORT ScDocument::GetOptimalColWidth( SCCOL nCol, SCTAB nTab, OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bFormula, const ScMarkData* pMarkData, + BOOL bSimpleTextImport ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetOptimalColWidth( nCol, pDev, nPPTX, nPPTY, + rZoomX, rZoomY, bFormula, pMarkData, bSimpleTextImport ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +long ScDocument::GetNeededSize( SCCOL nCol, SCROW nRow, SCTAB nTab, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bWidth, BOOL bTotalSize ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetNeededSize + ( nCol, nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, bTotalSize ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + + +BOOL ScDocument::SetOptimalHeight( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, USHORT nExtra, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bShrink ) +{ +//! MarkToMulti(); + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->SetOptimalHeight( nStartRow, nEndRow, nExtra, + pDev, nPPTX, nPPTY, rZoomX, rZoomY, bShrink ); + DBG_ERROR("Falsche Tabellennummer"); + return FALSE; +} + + +void ScDocument::UpdateAllRowHeights( OutputDevice* pDev, double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, const ScMarkData* pTabMark ) +{ + // one progress across all (selected) sheets + + ULONG nCellCount = 0; + for ( SCTAB nTab=0; nTab<=MAXTAB; nTab++ ) + if ( pTab[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) ) + nCellCount += pTab[nTab]->GetWeightedCount(); + + ScProgress aProgress( GetDocumentShell(), ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING), nCellCount ); + + ULONG nProgressStart = 0; + for ( SCTAB nTab=0; nTab<=MAXTAB; nTab++ ) + if ( pTab[nTab] && ( !pTabMark || pTabMark->GetTableSelect(nTab) ) ) + { + pTab[nTab]->SetOptimalHeight( 0, MAXROW, 0, + pDev, nPPTX, nPPTY, rZoomX, rZoomY, FALSE, &aProgress, nProgressStart ); + nProgressStart += pTab[nTab]->GetWeightedCount(); + } +} + + +// +// Spalten-/Zeilen-Flags ---------------------------------------------- +// + +void ScDocument::ShowCol(SCCOL nCol, SCTAB nTab, BOOL bShow) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ShowCol( nCol, bShow ); +} + + +void ScDocument::ShowRow(SCROW nRow, SCTAB nTab, BOOL bShow) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ShowRow( nRow, bShow ); +} + + +void ScDocument::ShowRows(SCROW nRow1, SCROW nRow2, SCTAB nTab, BOOL bShow) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ShowRows( nRow1, nRow2, bShow ); +} + + +void ScDocument::SetColFlags( SCCOL nCol, SCTAB nTab, BYTE nNewFlags ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetColFlags( nCol, nNewFlags ); +} + + +void ScDocument::SetRowFlags( SCROW nRow, SCTAB nTab, BYTE nNewFlags ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetRowFlags( nRow, nNewFlags ); +} + + +void ScDocument::SetRowFlags( SCROW nStartRow, SCROW nEndRow, SCTAB nTab, BYTE nNewFlags ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetRowFlags( nStartRow, nEndRow, nNewFlags ); +} + + +BYTE ScDocument::GetColFlags( SCCOL nCol, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetColFlags( nCol ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + +BYTE ScDocument::GetRowFlags( SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetRowFlags( nRow ); + DBG_ERROR("Falsche Tabellennummer"); + return 0; +} + +ScBitMaskCompressedArray< SCROW, BYTE> & ScDocument::GetRowFlagsArrayModifiable( + SCTAB nTab ) +{ + return const_cast< ScBitMaskCompressedArray< SCROW, BYTE> & >( + GetRowFlagsArray( nTab)); +} + +const ScBitMaskCompressedArray< SCROW, BYTE> & ScDocument::GetRowFlagsArray( + SCTAB nTab ) const +{ + const ScBitMaskCompressedArray< SCROW, BYTE> * pFlags; + if ( ValidTab(nTab) && pTab[nTab] ) + pFlags = pTab[nTab]->GetRowFlagsArray(); + else + { + DBG_ERROR("wrong sheet number"); + pFlags = 0; + } + if (!pFlags) + { + DBG_ERROR("no row flags at sheet"); + static ScBitMaskCompressedArray< SCROW, BYTE> aDummy( MAXROW, 0); + pFlags = &aDummy; + } + return *pFlags; +} + + +SCROW ScDocument::GetLastFlaggedRow( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetLastFlaggedRow(); + return 0; +} + + +SCCOL ScDocument::GetLastChangedCol( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetLastChangedCol(); + return 0; +} + +SCROW ScDocument::GetLastChangedRow( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetLastChangedRow(); + return 0; +} + + +SCCOL ScDocument::GetNextDifferentChangedCol( SCTAB nTab, SCCOL nStart) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + { + BYTE nStartFlags = pTab[nTab]->GetColFlags(nStart); + USHORT nStartWidth = pTab[nTab]->GetOriginalWidth(nStart); + for (SCCOL nCol = nStart + 1; nCol <= MAXCOL; nCol++) + { + if (((nStartFlags & CR_MANUALBREAK) != (pTab[nTab]->GetColFlags(nCol) & CR_MANUALBREAK)) || + (nStartWidth != pTab[nTab]->GetOriginalWidth(nCol)) || + ((nStartFlags & CR_HIDDEN) != (pTab[nTab]->GetColFlags(nCol) & CR_HIDDEN)) ) + return nCol; + } + return MAXCOL+1; + } + return 0; +} + +SCROW ScDocument::GetNextDifferentChangedRow( SCTAB nTab, SCROW nStart, bool bCareManualSize) const +{ + if ( ValidTab(nTab) && pTab[nTab] && pTab[nTab]->GetRowFlagsArray() && pTab[nTab]->GetRowHeightArray() ) + { + BYTE nStartFlags = pTab[nTab]->GetRowFlags(nStart); + USHORT nStartHeight = pTab[nTab]->GetOriginalHeight(nStart); + for (SCROW nRow = nStart + 1; nRow <= MAXROW; nRow++) + { + size_t nIndex; // ignored + SCROW nFlagsEndRow; + SCROW nHeightEndRow; + BYTE nFlags = pTab[nTab]->GetRowFlagsArray()->GetValue( nRow, nIndex, nFlagsEndRow ); + USHORT nHeight = pTab[nTab]->GetRowHeightArray()->GetValue( nRow, nIndex, nHeightEndRow ); + if (((nStartFlags & CR_MANUALBREAK) != (nFlags & CR_MANUALBREAK)) || + ((nStartFlags & CR_MANUALSIZE) != (nFlags & CR_MANUALSIZE)) || + (bCareManualSize && (nStartFlags & CR_MANUALSIZE) && (nStartHeight != nHeight)) || + (!bCareManualSize && ((nStartHeight != nHeight)))) + return nRow; + + nRow = std::min( nFlagsEndRow, nHeightEndRow ); + } + return MAXROW+1; + } + return 0; +} + +BOOL ScDocument::GetColDefault( SCTAB nTab, SCCOL nCol, SCROW nLastRow, SCROW& nDefault) +{ + BOOL bRet(FALSE); + nDefault = 0; + ScDocAttrIterator aDocAttrItr(this, nTab, nCol, 0, nCol, nLastRow); + SCCOL nColumn; + SCROW nStartRow; + SCROW nEndRow; + const ScPatternAttr* pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow); + if (nEndRow < nLastRow) + { + ScDefaultAttrSet aSet; + ScDefaultAttrSet::iterator aItr = aSet.end(); + while (pAttr) + { + ScDefaultAttr aAttr(pAttr); + aItr = aSet.find(aAttr); + if (aItr == aSet.end()) + { + aAttr.nCount = static_cast<SCSIZE>(nEndRow - nStartRow + 1); + aAttr.nFirst = nStartRow; + aSet.insert(aAttr); + } + else + { + aAttr.nCount = aItr->nCount + static_cast<SCSIZE>(nEndRow - nStartRow + 1); + aAttr.nFirst = aItr->nFirst; + aSet.erase(aItr); + aSet.insert(aAttr); + } + pAttr = aDocAttrItr.GetNext(nColumn, nStartRow, nEndRow); + } + ScDefaultAttrSet::iterator aDefaultItr = aSet.begin(); + aItr = aDefaultItr; + aItr++; + while (aItr != aSet.end()) + { + // for entries with equal count, use the one with the lowest start row, + // don't use the random order of pointer comparisons + if ( aItr->nCount > aDefaultItr->nCount || + ( aItr->nCount == aDefaultItr->nCount && aItr->nFirst < aDefaultItr->nFirst ) ) + aDefaultItr = aItr; + aItr++; + } + nDefault = aDefaultItr->nFirst; + bRet = TRUE; + } + else + bRet = TRUE; + return bRet; +} + +BOOL ScDocument::GetRowDefault( SCTAB /* nTab */, SCROW /* nRow */, SCCOL /* nLastCol */, SCCOL& /* nDefault */ ) +{ + BOOL bRet(FALSE); + return bRet; +} + +void ScDocument::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->StripHidden( rX1, rY1, rX2, rY2 ); +} + + +void ScDocument::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2, SCTAB nTab ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ExtendHidden( rX1, rY1, rX2, rY2 ); +} + +// +// Attribute ---------------------------------------------------------- +// + +const SfxPoolItem* ScDocument::GetAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, USHORT nWhich ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + { + const SfxPoolItem* pTemp = pTab[nTab]->GetAttr( nCol, nRow, nWhich ); + if (pTemp) + return pTemp; + else + { + DBG_ERROR( "Attribut Null" ); + } + } + return &xPoolHelper->GetDocPool()->GetDefaultItem( nWhich ); +} + + +const ScPatternAttr* ScDocument::GetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetPattern( nCol, nRow ); + return NULL; +} + + +const ScPatternAttr* ScDocument::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow, SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetMostUsedPattern( nCol, nStartRow, nEndRow ); + return NULL; +} + + +void ScDocument::ApplyAttr( SCCOL nCol, SCROW nRow, SCTAB nTab, const SfxPoolItem& rAttr ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ApplyAttr( nCol, nRow, rAttr ); +} + + +void ScDocument::ApplyPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->ApplyPattern( nCol, nRow, rAttr ); +} + + +void ScDocument::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, + const ScMarkData& rMark, + const ScPatternAttr& rAttr ) +{ + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr ); +} + + +void ScDocument::ApplyPatternAreaTab( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScPatternAttr& rAttr ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->ApplyPatternArea( nStartCol, nStartRow, nEndCol, nEndRow, rAttr ); +} + +void ScDocument::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange, + const ScMarkData& rMark, const ScPatternAttr& rPattern, short nNewType ) +{ + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType ); +} + + +void ScDocument::ApplyStyle( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScStyleSheet& rStyle) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->ApplyStyle( nCol, nRow, rStyle ); +} + + +void ScDocument::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, + const ScMarkData& rMark, + const ScStyleSheet& rStyle) +{ + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle ); +} + + +void ScDocument::ApplyStyleAreaTab( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, const ScStyleSheet& rStyle) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->ApplyStyleArea( nStartCol, nStartRow, nEndCol, nEndRow, rStyle ); +} + + +void ScDocument::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark) +{ + // ApplySelectionStyle needs multi mark + if ( rMark.IsMarked() && !rMark.IsMultiMarked() ) + { + ScRange aRange; + rMark.GetMarkArea( aRange ); + ApplyStyleArea( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rStyle ); + } + else + { + for (SCTAB i=0; i<=MAXTAB; i++) + if ( pTab[i] && rMark.GetTableSelect(i) ) + pTab[i]->ApplySelectionStyle( rStyle, rMark ); + } +} + + +void ScDocument::ApplySelectionLineStyle( const ScMarkData& rMark, + const SvxBorderLine* pLine, BOOL bColorOnly ) +{ + if ( bColorOnly && !pLine ) + return; + + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + pTab[i]->ApplySelectionLineStyle( rMark, pLine, bColorOnly ); +} + + +const ScStyleSheet* ScDocument::GetStyle( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + if ( VALIDTAB(nTab) && pTab[nTab] ) + return pTab[nTab]->GetStyle(nCol, nRow); + else + return NULL; +} + + +const ScStyleSheet* ScDocument::GetSelectionStyle( const ScMarkData& rMark ) const +{ + BOOL bEqual = TRUE; + BOOL bFound; + + const ScStyleSheet* pStyle = NULL; + const ScStyleSheet* pNewStyle; + + if ( rMark.IsMultiMarked() ) + for (SCTAB i=0; i<=MAXTAB && bEqual; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + { + pNewStyle = pTab[i]->GetSelectionStyle( rMark, bFound ); + if (bFound) + { + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + } + if ( rMark.IsMarked() ) + { + ScRange aRange; + rMark.GetMarkArea( aRange ); + for (SCTAB i=aRange.aStart.Tab(); i<=aRange.aEnd.Tab() && bEqual; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + { + pNewStyle = pTab[i]->GetAreaStyle( bFound, + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row() ); + if (bFound) + { + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + } + } + + return bEqual ? pStyle : NULL; +} + + +void ScDocument::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, BOOL bRemoved, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY ) +{ + for (SCTAB i=0; i <= MAXTAB; i++) + if (pTab[i]) + pTab[i]->StyleSheetChanged + ( pStyleSheet, bRemoved, pDev, nPPTX, nPPTY, rZoomX, rZoomY ); + + if ( pStyleSheet && pStyleSheet->GetName() == ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ) + { + // update attributes for all note objects + ScDetectiveFunc::UpdateAllComments( *this ); + } +} + + +BOOL ScDocument::IsStyleSheetUsed( const ScStyleSheet& rStyle, BOOL bGatherAllStyles ) const +{ + if ( bStyleSheetUsageInvalid || rStyle.GetUsage() == ScStyleSheet::UNKNOWN ) + { + if ( bGatherAllStyles ) + { + SfxStyleSheetIterator aIter( xPoolHelper->GetStylePool(), + SFX_STYLE_FAMILY_PARA ); + for ( const SfxStyleSheetBase* pStyle = aIter.First(); pStyle; + pStyle = aIter.Next() ) + { + const ScStyleSheet* pScStyle = PTR_CAST( ScStyleSheet, pStyle ); + if ( pScStyle ) + pScStyle->SetUsage( ScStyleSheet::NOTUSED ); + } + } + + BOOL bIsUsed = FALSE; + + for ( SCTAB i=0; i<=MAXTAB; i++ ) + { + if ( pTab[i] ) + { + if ( pTab[i]->IsStyleSheetUsed( rStyle, bGatherAllStyles ) ) + { + if ( !bGatherAllStyles ) + return TRUE; + bIsUsed = TRUE; + } + } + } + + if ( bGatherAllStyles ) + bStyleSheetUsageInvalid = FALSE; + + return bIsUsed; + } + + return rStyle.GetUsage() == ScStyleSheet::USED; +} + + +BOOL ScDocument::ApplyFlagsTab( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, INT16 nFlags ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->ApplyFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags ); + + DBG_ERROR("ApplyFlags: falsche Tabelle"); + return FALSE; +} + + +BOOL ScDocument::RemoveFlagsTab( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab, INT16 nFlags ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->RemoveFlags( nStartCol, nStartRow, nEndCol, nEndRow, nFlags ); + + DBG_ERROR("RemoveFlags: falsche Tabelle"); + return FALSE; +} + + +void ScDocument::SetPattern( SCCOL nCol, SCROW nRow, SCTAB nTab, const ScPatternAttr& rAttr, + BOOL bPutToPool ) +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + pTab[nTab]->SetPattern( nCol, nRow, rAttr, bPutToPool ); +} + + +void ScDocument::SetPattern( const ScAddress& rPos, const ScPatternAttr& rAttr, + BOOL bPutToPool ) +{ + SCTAB nTab = rPos.Tab(); + if (pTab[nTab]) + pTab[nTab]->SetPattern( rPos, rAttr, bPutToPool ); +} + + +ScPatternAttr* ScDocument::CreateSelectionPattern( const ScMarkData& rMark, BOOL bDeep ) +{ + ScMergePatternState aState; + + if ( rMark.IsMultiMarked() ) // multi selection + { + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->MergeSelectionPattern( aState, rMark, bDeep ); + } + if ( rMark.IsMarked() ) // simle selection + { + ScRange aRange; + rMark.GetMarkArea(aRange); + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->MergePatternArea( aState, + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), bDeep ); + } + + DBG_ASSERT( aState.pItemSet, "SelectionPattern Null" ); + if (aState.pItemSet) + return new ScPatternAttr( aState.pItemSet ); + else + return new ScPatternAttr( GetPool() ); // empty +} + + +const ScPatternAttr* ScDocument::GetSelectionPattern( const ScMarkData& rMark, BOOL bDeep ) +{ + delete pSelectionAttr; + pSelectionAttr = CreateSelectionPattern( rMark, bDeep ); + return pSelectionAttr; +} + + +void ScDocument::GetSelectionFrame( const ScMarkData& rMark, + SvxBoxItem& rLineOuter, + SvxBoxInfoItem& rLineInner ) +{ + rLineOuter.SetLine(NULL, BOX_LINE_TOP); + rLineOuter.SetLine(NULL, BOX_LINE_BOTTOM); + rLineOuter.SetLine(NULL, BOX_LINE_LEFT); + rLineOuter.SetLine(NULL, BOX_LINE_RIGHT); + rLineOuter.SetDistance(0); + + rLineInner.SetLine(NULL, BOXINFO_LINE_HORI); + rLineInner.SetLine(NULL, BOXINFO_LINE_VERT); + rLineInner.SetTable(TRUE); + rLineInner.SetDist(TRUE); + rLineInner.SetMinDist(FALSE); + + ScLineFlags aFlags; + + if (rMark.IsMarked()) + { + ScRange aRange; + rMark.GetMarkArea(aRange); + rLineInner.EnableHor( aRange.aStart.Row() != aRange.aEnd.Row() ); + rLineInner.EnableVer( aRange.aStart.Col() != aRange.aEnd.Col() ); + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->MergeBlockFrame( &rLineOuter, &rLineInner, aFlags, + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row() ); + } + + // Don't care Status auswerten + + rLineInner.SetValid( VALID_LEFT, ( aFlags.nLeft != SC_LINE_DONTCARE ) ); + rLineInner.SetValid( VALID_RIGHT, ( aFlags.nRight != SC_LINE_DONTCARE ) ); + rLineInner.SetValid( VALID_TOP, ( aFlags.nTop != SC_LINE_DONTCARE ) ); + rLineInner.SetValid( VALID_BOTTOM, ( aFlags.nBottom != SC_LINE_DONTCARE ) ); + rLineInner.SetValid( VALID_HORI, ( aFlags.nHori != SC_LINE_DONTCARE ) ); + rLineInner.SetValid( VALID_VERT, ( aFlags.nVert != SC_LINE_DONTCARE ) ); +} + + +BOOL ScDocument::HasAttrib( SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, USHORT nMask ) +{ + if ( nMask & HASATTR_ROTATE ) + { + // Attribut im Dokument ueberhaupt verwendet? + // (wie in fillinfo) + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + + BOOL bAnyItem = FALSE; + USHORT nRotCount = pPool->GetItemCount( ATTR_ROTATE_VALUE ); + for (USHORT nItem=0; nItem<nRotCount; nItem++) + { + const SfxPoolItem* pItem = pPool->GetItem( ATTR_ROTATE_VALUE, nItem ); + if ( pItem ) + { + // 90 or 270 degrees is former SvxOrientationItem - only look for other values + // (see ScPatternAttr::GetCellOrientation) + INT32 nAngle = static_cast<const SfxInt32Item*>(pItem)->GetValue(); + if ( nAngle != 0 && nAngle != 9000 && nAngle != 27000 ) + { + bAnyItem = TRUE; + break; + } + } + } + if (!bAnyItem) + nMask &= ~HASATTR_ROTATE; + } + + if ( nMask & HASATTR_RTL ) + { + // first check if right-to left is in the pool at all + // (the same item is used in cell and page format) + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + + BOOL bHasRtl = FALSE; + USHORT nDirCount = pPool->GetItemCount( ATTR_WRITINGDIR ); + for (USHORT nItem=0; nItem<nDirCount; nItem++) + { + const SfxPoolItem* pItem = pPool->GetItem( ATTR_WRITINGDIR, nItem ); + if ( pItem && ((const SvxFrameDirectionItem*)pItem)->GetValue() == FRMDIR_HORI_RIGHT_TOP ) + { + bHasRtl = TRUE; + break; + } + } + if (!bHasRtl) + nMask &= ~HASATTR_RTL; + } + + if (!nMask) + return FALSE; + + BOOL bFound = FALSE; + for (SCTAB i=nTab1; i<=nTab2 && !bFound; i++) + if (pTab[i]) + { + if ( nMask & HASATTR_RTL ) + { + if ( GetEditTextDirection(i) == EE_HTEXTDIR_R2L ) // sheet default + bFound = TRUE; + } + if ( nMask & HASATTR_RIGHTORCENTER ) + { + // On a RTL sheet, don't start to look for the default left value + // (which is then logically right), instead always assume TRUE. + // That way, ScAttrArray::HasAttrib doesn't have to handle RTL sheets. + + if ( IsLayoutRTL(i) ) + bFound = TRUE; + } + + if ( !bFound ) + bFound = pTab[i]->HasAttrib( nCol1, nRow1, nCol2, nRow2, nMask ); + } + + return bFound; +} + +BOOL ScDocument::HasAttrib( const ScRange& rRange, USHORT nMask ) +{ + return HasAttrib( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), + nMask ); +} + +void ScDocument::FindMaxRotCol( SCTAB nTab, RowInfo* pRowInfo, SCSIZE nArrCount, + SCCOL nX1, SCCOL nX2 ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->FindMaxRotCol( pRowInfo, nArrCount, nX1, nX2 ); + else + { + DBG_ERRORFILE("FindMaxRotCol: falsche Tabelle"); + } +} + +void ScDocument::GetBorderLines( SCCOL nCol, SCROW nRow, SCTAB nTab, + const SvxBorderLine** ppLeft, const SvxBorderLine** ppTop, + const SvxBorderLine** ppRight, const SvxBorderLine** ppBottom ) const +{ + //! Seitengrenzen fuer Druck beruecksichtigen !!!!! + + const SvxBoxItem* pThisAttr = (const SvxBoxItem*) GetEffItem( nCol, nRow, nTab, ATTR_BORDER ); + DBG_ASSERT(pThisAttr,"wo ist das Attribut?"); + + const SvxBorderLine* pLeftLine = pThisAttr->GetLeft(); + const SvxBorderLine* pTopLine = pThisAttr->GetTop(); + const SvxBorderLine* pRightLine = pThisAttr->GetRight(); + const SvxBorderLine* pBottomLine = pThisAttr->GetBottom(); + + if ( nCol > 0 ) + { + const SvxBorderLine* pOther = ((const SvxBoxItem*) + GetEffItem( nCol-1, nRow, nTab, ATTR_BORDER ))->GetRight(); + if ( ScHasPriority( pOther, pLeftLine ) ) + pLeftLine = pOther; + } + if ( nRow > 0 ) + { + const SvxBorderLine* pOther = ((const SvxBoxItem*) + GetEffItem( nCol, nRow-1, nTab, ATTR_BORDER ))->GetBottom(); + if ( ScHasPriority( pOther, pTopLine ) ) + pTopLine = pOther; + } + if ( nCol < MAXCOL ) + { + const SvxBorderLine* pOther = ((const SvxBoxItem*) + GetEffItem( nCol+1, nRow, nTab, ATTR_BORDER ))->GetLeft(); + if ( ScHasPriority( pOther, pRightLine ) ) + pRightLine = pOther; + } + if ( nRow < MAXROW ) + { + const SvxBorderLine* pOther = ((const SvxBoxItem*) + GetEffItem( nCol, nRow+1, nTab, ATTR_BORDER ))->GetTop(); + if ( ScHasPriority( pOther, pBottomLine ) ) + pBottomLine = pOther; + } + + if (ppLeft) + *ppLeft = pLeftLine; + if (ppTop) + *ppTop = pTopLine; + if (ppRight) + *ppRight = pRightLine; + if (ppBottom) + *ppBottom = pBottomLine; +} + +BOOL ScDocument::IsBlockEmpty( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, bool bIgnoreNotes ) const +{ + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->IsBlockEmpty( nStartCol, nStartRow, nEndCol, nEndRow, bIgnoreNotes ); + + DBG_ERROR("Falsche Tabellennummer"); + return FALSE; +} + + +void ScDocument::LockTable(SCTAB nTab) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->LockTable(); + else + { + DBG_ERROR("Falsche Tabellennummer"); + } +} + + +void ScDocument::UnlockTable(SCTAB nTab) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->UnlockTable(); + else + { + DBG_ERROR("Falsche Tabellennummer"); + } +} + + +BOOL ScDocument::IsBlockEditable( SCTAB nTab, SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, + BOOL* pOnlyNotBecauseOfMatrix /* = NULL */ ) const +{ + // import into read-only document is possible + if ( !bImportingXML && !mbChangeReadOnlyEnabled && pShell && pShell->IsReadOnly() ) + { + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + return FALSE; + } + + if (VALIDTAB(nTab)) + if (pTab[nTab]) + return pTab[nTab]->IsBlockEditable( nStartCol, nStartRow, nEndCol, + nEndRow, pOnlyNotBecauseOfMatrix ); + + DBG_ERROR("Falsche Tabellennummer"); + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + return FALSE; +} + + +BOOL ScDocument::IsSelectionEditable( const ScMarkData& rMark, + BOOL* pOnlyNotBecauseOfMatrix /* = NULL */ ) const +{ + // import into read-only document is possible + if ( !bImportingXML && !mbChangeReadOnlyEnabled && pShell && pShell->IsReadOnly() ) + { + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + return FALSE; + } + + ScRange aRange; + rMark.GetMarkArea(aRange); + + BOOL bOk = TRUE; + BOOL bMatrix = ( pOnlyNotBecauseOfMatrix != NULL ); + for ( SCTAB i=0; i<=MAXTAB && (bOk || bMatrix); i++ ) + { + if ( pTab[i] && rMark.GetTableSelect(i) ) + { + if (rMark.IsMarked()) + { + if ( !pTab[i]->IsBlockEditable( aRange.aStart.Col(), + aRange.aStart.Row(), aRange.aEnd.Col(), + aRange.aEnd.Row(), pOnlyNotBecauseOfMatrix ) ) + { + bOk = FALSE; + if ( pOnlyNotBecauseOfMatrix ) + bMatrix = *pOnlyNotBecauseOfMatrix; + } + } + if (rMark.IsMultiMarked()) + { + if ( !pTab[i]->IsSelectionEditable( rMark, pOnlyNotBecauseOfMatrix ) ) + { + bOk = FALSE; + if ( pOnlyNotBecauseOfMatrix ) + bMatrix = *pOnlyNotBecauseOfMatrix; + } + } + } + } + + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = ( !bOk && bMatrix ); + + return bOk; +} + + +BOOL ScDocument::HasSelectedBlockMatrixFragment( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, + const ScMarkData& rMark ) const +{ + BOOL bOk = TRUE; + for (SCTAB i=0; i<=MAXTAB && bOk; i++) + if (pTab[i]) + if (rMark.GetTableSelect(i)) + if (pTab[i]->HasBlockMatrixFragment( nStartCol, nStartRow, nEndCol, nEndRow )) + bOk = FALSE; + + return !bOk; +} + + +BOOL ScDocument::GetMatrixFormulaRange( const ScAddress& rCellPos, ScRange& rMatrix ) +{ + // if rCell is part of a matrix formula, return its complete range + + BOOL bRet = FALSE; + ScBaseCell* pCell = GetCell( rCellPos ); + if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) + { + ScAddress aOrigin = rCellPos; + if ( ((ScFormulaCell*)pCell)->GetMatrixOrigin( aOrigin ) ) + { + if ( aOrigin != rCellPos ) + pCell = GetCell( aOrigin ); + if (pCell && pCell->GetCellType() == CELLTYPE_FORMULA) + { + SCCOL nSizeX; + SCROW nSizeY; + ((ScFormulaCell*)pCell)->GetMatColsRows(nSizeX,nSizeY); + if ( !(nSizeX > 0 && nSizeY > 0) ) + { + // GetMatrixEdge computes also dimensions of the matrix + // if not already done (may occur if document is loaded + // from old file format). + // Needs an "invalid" initialized address. + aOrigin.SetInvalid(); + ((ScFormulaCell*)pCell)->GetMatrixEdge(aOrigin); + ((ScFormulaCell*)pCell)->GetMatColsRows(nSizeX,nSizeY); + } + if ( nSizeX > 0 && nSizeY > 0 ) + { + ScAddress aEnd( aOrigin.Col() + nSizeX - 1, + aOrigin.Row() + nSizeY - 1, + aOrigin.Tab() ); + + rMatrix.aStart = aOrigin; + rMatrix.aEnd = aEnd; + bRet = TRUE; + } + } + } + } + return bRet; +} + + +BOOL ScDocument::ExtendOverlapped( SCCOL& rStartCol, SCROW& rStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) +{ + BOOL bFound = FALSE; + if ( ValidColRow(rStartCol,rStartRow) && ValidColRow(nEndCol,nEndRow) && ValidTab(nTab) ) + { + if (pTab[nTab]) + { + SCCOL nCol; + SCCOL nOldCol = rStartCol; + SCROW nOldRow = rStartRow; + for (nCol=nOldCol; nCol<=nEndCol; nCol++) + while (((ScMergeFlagAttr*)GetAttr(nCol,rStartRow,nTab,ATTR_MERGE_FLAG))-> + IsVerOverlapped()) + --rStartRow; + + //! weiterreichen ? + + ScAttrArray* pAttrArray = pTab[nTab]->aCol[nOldCol].pAttrArray; + SCSIZE nIndex; + pAttrArray->Search( nOldRow, nIndex ); + SCROW nAttrPos = nOldRow; + while (nAttrPos<=nEndRow) + { + DBG_ASSERT( nIndex < pAttrArray->nCount, "Falscher Index im AttrArray" ); + + if (((ScMergeFlagAttr&)pAttrArray->pData[nIndex].pPattern-> + GetItem(ATTR_MERGE_FLAG)).IsHorOverlapped()) + { + SCROW nLoopEndRow = Min( nEndRow, pAttrArray->pData[nIndex].nRow ); + for (SCROW nAttrRow = nAttrPos; nAttrRow <= nLoopEndRow; nAttrRow++) + { + SCCOL nTempCol = nOldCol; + do + --nTempCol; + while (((ScMergeFlagAttr*)GetAttr(nTempCol,nAttrRow,nTab,ATTR_MERGE_FLAG)) + ->IsHorOverlapped()); + if (nTempCol < rStartCol) + rStartCol = nTempCol; + } + } + nAttrPos = pAttrArray->pData[nIndex].nRow + 1; + ++nIndex; + } + } + } + else + { + DBG_ERROR("ExtendOverlapped: falscher Bereich"); + } + + return bFound; +} + + +BOOL ScDocument::ExtendMergeSel( SCCOL nStartCol, SCROW nStartRow, + SCCOL& rEndCol, SCROW& rEndRow, + const ScMarkData& rMark, BOOL bRefresh, BOOL bAttrs ) +{ + // use all selected sheets from rMark + + BOOL bFound = FALSE; + SCCOL nOldEndCol = rEndCol; + SCROW nOldEndRow = rEndRow; + + for (SCTAB nTab = 0; nTab <= MAXTAB; nTab++) + if ( pTab[nTab] && rMark.GetTableSelect(nTab) ) + { + SCCOL nThisEndCol = nOldEndCol; + SCROW nThisEndRow = nOldEndRow; + if ( ExtendMerge( nStartCol, nStartRow, nThisEndCol, nThisEndRow, nTab, bRefresh, bAttrs ) ) + bFound = TRUE; + if ( nThisEndCol > rEndCol ) + rEndCol = nThisEndCol; + if ( nThisEndRow > rEndRow ) + rEndRow = nThisEndRow; + } + + return bFound; +} + + +BOOL ScDocument::ExtendMerge( SCCOL nStartCol, SCROW nStartRow, + SCCOL& rEndCol, SCROW& rEndRow, + SCTAB nTab, BOOL bRefresh, BOOL bAttrs ) +{ + BOOL bFound = FALSE; + if ( ValidColRow(nStartCol,nStartRow) && ValidColRow(rEndCol,rEndRow) && ValidTab(nTab) ) + { + if (pTab[nTab]) + bFound = pTab[nTab]->ExtendMerge( nStartCol, nStartRow, rEndCol, rEndRow, bRefresh, bAttrs ); + + if (bRefresh) + RefreshAutoFilter( nStartCol, nStartRow, rEndCol, rEndRow, nTab ); + } + else + { + DBG_ERROR("ExtendMerge: falscher Bereich"); + } + + return bFound; +} + + +BOOL ScDocument::ExtendMerge( ScRange& rRange, BOOL bRefresh, BOOL bAttrs ) +{ + BOOL bFound = FALSE; + SCTAB nStartTab = rRange.aStart.Tab(); + SCTAB nEndTab = rRange.aEnd.Tab(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + + PutInOrder( nStartTab, nEndTab ); + for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++ ) + { + SCCOL nExtendCol = rRange.aEnd.Col(); + SCROW nExtendRow = rRange.aEnd.Row(); + if (ExtendMerge( rRange.aStart.Col(), rRange.aStart.Row(), + nExtendCol, nExtendRow, + nTab, bRefresh, bAttrs ) ) + { + bFound = TRUE; + if (nExtendCol > nEndCol) nEndCol = nExtendCol; + if (nExtendRow > nEndRow) nEndRow = nExtendRow; + } + } + + rRange.aEnd.SetCol(nEndCol); + rRange.aEnd.SetRow(nEndRow); + + return bFound; +} + +BOOL ScDocument::ExtendTotalMerge( ScRange& rRange ) +{ + // Bereich genau dann auf zusammengefasste Zellen erweitern, wenn + // dadurch keine neuen nicht-ueberdeckten Zellen getroffen werden + + BOOL bRet = FALSE; + ScRange aExt = rRange; + if (ExtendMerge(aExt)) + { + if ( aExt.aEnd.Row() > rRange.aEnd.Row() ) + { + ScRange aTest = aExt; + aTest.aStart.SetRow( rRange.aEnd.Row() + 1 ); + if ( HasAttrib( aTest, HASATTR_NOTOVERLAPPED ) ) + aExt.aEnd.SetRow(rRange.aEnd.Row()); + } + if ( aExt.aEnd.Col() > rRange.aEnd.Col() ) + { + ScRange aTest = aExt; + aTest.aStart.SetCol( rRange.aEnd.Col() + 1 ); + if ( HasAttrib( aTest, HASATTR_NOTOVERLAPPED ) ) + aExt.aEnd.SetCol(rRange.aEnd.Col()); + } + + bRet = ( aExt.aEnd != rRange.aEnd ); + rRange = aExt; + } + return bRet; +} + +BOOL ScDocument::ExtendOverlapped( ScRange& rRange ) +{ + BOOL bFound = FALSE; + SCTAB nStartTab = rRange.aStart.Tab(); + SCTAB nEndTab = rRange.aEnd.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + + PutInOrder( nStartTab, nEndTab ); + for (SCTAB nTab = nStartTab; nTab <= nEndTab; nTab++ ) + { + SCCOL nExtendCol = rRange.aStart.Col(); + SCROW nExtendRow = rRange.aStart.Row(); + ExtendOverlapped( nExtendCol, nExtendRow, + rRange.aEnd.Col(), rRange.aEnd.Row(), nTab ); + if (nExtendCol < nStartCol) + { + nStartCol = nExtendCol; + bFound = TRUE; + } + if (nExtendRow < nStartRow) + { + nStartRow = nExtendRow; + bFound = TRUE; + } + } + + rRange.aStart.SetCol(nStartCol); + rRange.aStart.SetRow(nStartRow); + + return bFound; +} + +BOOL ScDocument::RefreshAutoFilter( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, SCTAB nTab ) +{ + USHORT nCount = pDBCollection->GetCount(); + USHORT i; + ScDBData* pData; + SCTAB nDBTab; + SCCOL nDBStartCol; + SCROW nDBStartRow; + SCCOL nDBEndCol; + SCROW nDBEndRow; + + // Autofilter loeschen + + BOOL bChange = RemoveFlagsTab( nStartCol,nStartRow, nEndCol,nEndRow, nTab, SC_MF_AUTO ); + + // Autofilter setzen + + for (i=0; i<nCount; i++) + { + pData = (*pDBCollection)[i]; + if (pData->HasAutoFilter()) + { + pData->GetArea( nDBTab, nDBStartCol,nDBStartRow, nDBEndCol,nDBEndRow ); + if ( nDBTab==nTab && nDBStartRow<=nEndRow && nDBEndRow>=nStartRow && + nDBStartCol<=nEndCol && nDBEndCol>=nStartCol ) + { + if (ApplyFlagsTab( nDBStartCol,nDBStartRow, nDBEndCol,nDBStartRow, + nDBTab, SC_MF_AUTO )) + bChange = TRUE; + } + } + } + return bChange; +} + + +BOOL ScDocument::IsHorOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + const ScMergeFlagAttr* pAttr = (const ScMergeFlagAttr*) + GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ); + if (pAttr) + return pAttr->IsHorOverlapped(); + else + { + DBG_ERROR("Overlapped: Attr==0"); + return FALSE; + } +} + + +BOOL ScDocument::IsVerOverlapped( SCCOL nCol, SCROW nRow, SCTAB nTab ) const +{ + const ScMergeFlagAttr* pAttr = (const ScMergeFlagAttr*) + GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ); + if (pAttr) + return pAttr->IsVerOverlapped(); + else + { + DBG_ERROR("Overlapped: Attr==0"); + return FALSE; + } +} + + +void ScDocument::ApplySelectionFrame( const ScMarkData& rMark, + const SvxBoxItem* pLineOuter, + const SvxBoxInfoItem* pLineInner ) +{ + ScRangeList aRangeList; + rMark.FillRangeListWithMarks( &aRangeList, FALSE ); + ULONG nRangeCount = aRangeList.Count(); + for (SCTAB i=0; i<=MAXTAB; i++) + { + if (pTab[i] && rMark.GetTableSelect(i)) + { + for (ULONG j=0; j<nRangeCount; j++) + { + ScRange aRange = *aRangeList.GetObject(j); + pTab[i]->ApplyBlockFrame( pLineOuter, pLineInner, + aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row() ); + } + } + } +} + + +void ScDocument::ApplyFrameAreaTab( const ScRange& rRange, + const SvxBoxItem* pLineOuter, + const SvxBoxInfoItem* pLineInner ) +{ + SCTAB nStartTab = rRange.aStart.Tab(); + SCTAB nEndTab = rRange.aStart.Tab(); + for (SCTAB nTab=nStartTab; nTab<=nEndTab; nTab++) + if (pTab[nTab]) + pTab[nTab]->ApplyBlockFrame( pLineOuter, pLineInner, + rRange.aStart.Col(), rRange.aStart.Row(), + rRange.aEnd.Col(), rRange.aEnd.Row() ); +} + + +void ScDocument::ApplySelectionPattern( const ScPatternAttr& rAttr, const ScMarkData& rMark ) +{ + const SfxItemSet* pSet = &rAttr.GetItemSet(); + BOOL bSet = FALSE; + USHORT i; + for (i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END && !bSet; i++) + if (pSet->GetItemState(i) == SFX_ITEM_SET) + bSet = TRUE; + + if (bSet) + { + // ApplySelectionCache needs multi mark + if ( rMark.IsMarked() && !rMark.IsMultiMarked() ) + { + ScRange aRange; + rMark.GetMarkArea( aRange ); + ApplyPatternArea( aRange.aStart.Col(), aRange.aStart.Row(), + aRange.aEnd.Col(), aRange.aEnd.Row(), rMark, rAttr ); + } + else + { + SfxItemPoolCache aCache( xPoolHelper->GetDocPool(), pSet ); + for (SCTAB nTab=0; nTab<=MAXTAB; nTab++) + if (pTab[nTab]) + if (rMark.GetTableSelect(nTab)) + pTab[nTab]->ApplySelectionCache( &aCache, rMark ); + } + } +} + + +void ScDocument::ChangeSelectionIndent( BOOL bIncrement, const ScMarkData& rMark ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->ChangeSelectionIndent( bIncrement, rMark ); +} + + +void ScDocument::ClearSelectionItems( const USHORT* pWhich, const ScMarkData& rMark ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->ClearSelectionItems( pWhich, rMark ); +} + + +void ScDocument::DeleteSelection( USHORT nDelFlag, const ScMarkData& rMark ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + if (pTab[i] && rMark.GetTableSelect(i)) + pTab[i]->DeleteSelection( nDelFlag, rMark ); +} + + +void ScDocument::DeleteSelectionTab( SCTAB nTab, USHORT nDelFlag, const ScMarkData& rMark ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->DeleteSelection( nDelFlag, rMark ); + else + { + DBG_ERROR("Falsche Tabelle"); + } +} + + +ScPatternAttr* ScDocument::GetDefPattern() const +{ + return (ScPatternAttr*) &xPoolHelper->GetDocPool()->GetDefaultItem(ATTR_PATTERN); +} + + +ScDocumentPool* ScDocument::GetPool() +{ + return xPoolHelper->GetDocPool(); +} + + + +ScStyleSheetPool* ScDocument::GetStyleSheetPool() const +{ + return xPoolHelper->GetStylePool(); +} + + +SCSIZE ScDocument::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, SCTAB nStartTab, + SCCOL nEndCol, SCROW nEndRow, SCTAB nEndTab, ScDirection eDir ) +{ + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + PutInOrder(nStartTab, nEndTab); + if (VALIDTAB(nStartTab)) + { + if (pTab[nStartTab]) + return pTab[nStartTab]->GetEmptyLinesInBlock(nStartCol, nStartRow, nEndCol, nEndRow, eDir); + else + return 0; + } + else + return 0; +} + + +void ScDocument::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCsCOL nMovX, SCsROW nMovY ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->FindAreaPos( rCol, rRow, nMovX, nMovY ); +} + + +void ScDocument::GetNextPos( SCCOL& rCol, SCROW& rRow, SCTAB nTab, SCsCOL nMovX, SCsROW nMovY, + BOOL bMarked, BOOL bUnprotected, const ScMarkData& rMark ) +{ + DBG_ASSERT( !nMovX || !nMovY, "GetNextPos: nur X oder Y" ); + + ScMarkData aCopyMark = rMark; + aCopyMark.SetMarking(FALSE); + aCopyMark.MarkToMulti(); + + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->GetNextPos( rCol, rRow, nMovX, nMovY, bMarked, bUnprotected, aCopyMark ); +} + +// +// Datei-Operationen +// + + +void ScDocument::UpdStlShtPtrsFrmNms() +{ + ScPatternAttr::pDoc = this; + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + + USHORT nCount = pPool->GetItemCount(ATTR_PATTERN); + ScPatternAttr* pPattern; + for (USHORT i=0; i<nCount; i++) + { + pPattern = (ScPatternAttr*)pPool->GetItem(ATTR_PATTERN, i); + if (pPattern) + pPattern->UpdateStyleSheet(); + } + ((ScPatternAttr&)pPool->GetDefaultItem(ATTR_PATTERN)).UpdateStyleSheet(); +} + + +void ScDocument::StylesToNames() +{ + ScPatternAttr::pDoc = this; + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + + USHORT nCount = pPool->GetItemCount(ATTR_PATTERN); + ScPatternAttr* pPattern; + for (USHORT i=0; i<nCount; i++) + { + pPattern = (ScPatternAttr*)pPool->GetItem(ATTR_PATTERN, i); + if (pPattern) + pPattern->StyleToName(); + } + ((ScPatternAttr&)pPool->GetDefaultItem(ATTR_PATTERN)).StyleToName(); +} + + +ULONG ScDocument::GetCellCount() const +{ + ULONG nCellCount = 0L; + + for ( SCTAB nTab=0; nTab<=MAXTAB; nTab++ ) + if ( pTab[nTab] ) + nCellCount += pTab[nTab]->GetCellCount(); + + return nCellCount; +} + + +ULONG ScDocument::GetCodeCount() const +{ + ULONG nCodeCount = 0; + + for ( SCTAB nTab=0; nTab<=MAXTAB; nTab++ ) + if ( pTab[nTab] ) + nCodeCount += pTab[nTab]->GetCodeCount(); + + return nCodeCount; +} + + +ULONG ScDocument::GetWeightedCount() const +{ + ULONG nCellCount = 0L; + + for ( SCTAB nTab=0; nTab<=MAXTAB; nTab++ ) + if ( pTab[nTab] ) + nCellCount += pTab[nTab]->GetWeightedCount(); + + return nCellCount; +} + + +void ScDocument::PageStyleModified( SCTAB nTab, const String& rNewName ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->PageStyleModified( rNewName ); +} + + +void ScDocument::SetPageStyle( SCTAB nTab, const String& rName ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetPageStyle( rName ); +} + + +const String& ScDocument::GetPageStyle( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetPageStyle(); + + return EMPTY_STRING; +} + + +void ScDocument::SetPageSize( SCTAB nTab, const Size& rSize ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetPageSize( rSize ); +} + +Size ScDocument::GetPageSize( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->GetPageSize(); + + DBG_ERROR("falsche Tab"); + return Size(); +} + + +void ScDocument::SetRepeatArea( SCTAB nTab, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->SetRepeatArea( nStartCol, nEndCol, nStartRow, nEndRow ); +} + + +void ScDocument::UpdatePageBreaks( SCTAB nTab, const ScRange* pUserArea ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->UpdatePageBreaks( pUserArea ); +} + +void ScDocument::RemoveManualBreaks( SCTAB nTab ) +{ + if ( ValidTab(nTab) && pTab[nTab] ) + pTab[nTab]->RemoveManualBreaks(); +} + +BOOL ScDocument::HasManualBreaks( SCTAB nTab ) const +{ + if ( ValidTab(nTab) && pTab[nTab] ) + return pTab[nTab]->HasManualBreaks(); + + DBG_ERROR("falsche Tab"); + return FALSE; +} + + +void ScDocument::GetDocStat( ScDocStat& rDocStat ) +{ + rDocStat.nTableCount = GetTableCount(); + rDocStat.aDocName = aDocName; + rDocStat.nCellCount = GetCellCount(); +} + + +BOOL ScDocument::HasPrintRange() +{ + BOOL bResult = FALSE; + + for ( SCTAB i=0; !bResult && i<nMaxTableNumber; i++ ) + if ( pTab[i] ) + bResult = pTab[i]->IsPrintEntireSheet() || (pTab[i]->GetPrintRangeCount() > 0); + + return bResult; +} + + +BOOL ScDocument::IsPrintEntireSheet( SCTAB nTab ) const +{ + return (ValidTab(nTab) ) && pTab[nTab] && pTab[nTab]->IsPrintEntireSheet(); +} + + +USHORT ScDocument::GetPrintRangeCount( SCTAB nTab ) +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetPrintRangeCount(); + + return 0; +} + + +const ScRange* ScDocument::GetPrintRange( SCTAB nTab, USHORT nPos ) +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetPrintRange(nPos); + + return NULL; +} + + +const ScRange* ScDocument::GetRepeatColRange( SCTAB nTab ) +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetRepeatColRange(); + + return NULL; +} + + +const ScRange* ScDocument::GetRepeatRowRange( SCTAB nTab ) +{ + if (ValidTab(nTab) && pTab[nTab]) + return pTab[nTab]->GetRepeatRowRange(); + + return NULL; +} + + +void ScDocument::ClearPrintRanges( SCTAB nTab ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->ClearPrintRanges(); +} + + +void ScDocument::AddPrintRange( SCTAB nTab, const ScRange& rNew ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->AddPrintRange( rNew ); +} + + +//UNUSED2009-05 void ScDocument::SetPrintRange( SCTAB nTab, const ScRange& rNew ) +//UNUSED2009-05 { +//UNUSED2009-05 if (ValidTab(nTab) && pTab[nTab]) +//UNUSED2009-05 pTab[nTab]->SetPrintRange( rNew ); +//UNUSED2009-05 } + + +void ScDocument::SetPrintEntireSheet( SCTAB nTab ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetPrintEntireSheet(); +} + + +void ScDocument::SetRepeatColRange( SCTAB nTab, const ScRange* pNew ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetRepeatColRange( pNew ); +} + + +void ScDocument::SetRepeatRowRange( SCTAB nTab, const ScRange* pNew ) +{ + if (ValidTab(nTab) && pTab[nTab]) + pTab[nTab]->SetRepeatRowRange( pNew ); +} + + +ScPrintRangeSaver* ScDocument::CreatePrintRangeSaver() const +{ + SCTAB nCount = GetTableCount(); + ScPrintRangeSaver* pNew = new ScPrintRangeSaver( nCount ); + for (SCTAB i=0; i<nCount; i++) + if (pTab[i]) + pTab[i]->FillPrintSaver( pNew->GetTabData(i) ); + return pNew; +} + + +void ScDocument::RestorePrintRanges( const ScPrintRangeSaver& rSaver ) +{ + SCTAB nCount = rSaver.GetTabCount(); + for (SCTAB i=0; i<nCount; i++) + if (pTab[i]) + pTab[i]->RestorePrintRanges( rSaver.GetTabData(i) ); +} + + +BOOL ScDocument::NeedPageResetAfterTab( SCTAB nTab ) const +{ + // Die Seitennummern-Zaehlung faengt bei einer Tabelle neu an, wenn eine + // andere Vorlage als bei der vorherigen gesetzt ist (nur Namen vergleichen) + // und eine Seitennummer angegeben ist (nicht 0) + + if ( nTab < MAXTAB && pTab[nTab] && pTab[nTab+1] ) + { + String aNew = pTab[nTab+1]->GetPageStyle(); + if ( aNew != pTab[nTab]->GetPageStyle() ) + { + SfxStyleSheetBase* pStyle = xPoolHelper->GetStylePool()->Find( aNew, SFX_STYLE_FAMILY_PAGE ); + if ( pStyle ) + { + const SfxItemSet& rSet = pStyle->GetItemSet(); + USHORT nFirst = ((const SfxUInt16Item&)rSet.Get(ATTR_PAGE_FIRSTPAGENO)).GetValue(); + if ( nFirst != 0 ) + return TRUE; // Seitennummer in neuer Vorlage angegeben + } + } + } + + return FALSE; // sonst nicht +} + +SfxUndoManager* ScDocument::GetUndoManager() +{ + if (!mpUndoManager) + mpUndoManager = new SfxUndoManager; + return mpUndoManager; +} + + +void ScDocument::EnableUndo( bool bVal ) +{ + GetUndoManager()->EnableUndo(bVal); + mbUndoEnabled = bVal; +} + + diff --git a/sc/source/core/data/dpcachetable.cxx b/sc/source/core/data/dpcachetable.cxx new file mode 100644 index 000000000000..dc9b6450ac75 --- /dev/null +++ b/sc/source/core/data/dpcachetable.cxx @@ -0,0 +1,691 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpcachetable.cxx,v $ + * + * $Revision: 1.6 $ + * + * 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 "dpcachetable.hxx" +#include "document.hxx" +#include "address.hxx" +#include "cell.hxx" +#include "dptabdat.hxx" +#include "dptabsrc.hxx" +#include "dpobject.hxx" + +#include <com/sun/star/i18n/LocaleDataItem.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaData.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> + +#include <memory> + +using namespace ::com::sun::star; + +using ::rtl::OUString; +using ::std::vector; +using ::std::pair; +using ::std::hash_map; +using ::std::hash_set; +using ::std::auto_ptr; +using ::com::sun::star::i18n::LocaleDataItem; +using ::com::sun::star::uno::Exception; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::com::sun::star::sheet::DataPilotFieldFilter; + +const double D_TIMEFACTOR = 86400.0; + +static BOOL lcl_HasQueryEntry( const ScQueryParam& rParam ) +{ + return rParam.GetEntryCount() > 0 && + rParam.GetEntry(0).bDoQuery; +} + +// ---------------------------------------------------------------------------- + +static ScDPCacheCell EmptyCellContent = ScDPCacheCell(); + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::Cell::Cell() : + mnCategoryRef(0), + mpContent(NULL) +{ +} + +ScDPCacheTable::Cell::~Cell() +{ +} + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::FilterItem::FilterItem() : + mnMatchStrId(ScSimpleSharedString::EMPTY), + mfValue(0.0), + mbHasValue(false) +{ +} + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::SingleFilter::SingleFilter(ScSimpleSharedString& rSharedString, + sal_Int32 nMatchStrId, double fValue, bool bHasValue) : + mrSharedString(rSharedString) +{ + maItem.mnMatchStrId = nMatchStrId; + maItem.mfValue = fValue; + maItem.mbHasValue = bHasValue; +} + +bool ScDPCacheTable::SingleFilter::match(const ScDPCacheCell& rCell) const +{ + if (rCell.mnStrId != maItem.mnMatchStrId && + (!rCell.mbNumeric || rCell.mfValue != maItem.mfValue)) + return false; + + return true; +} + +const String ScDPCacheTable::SingleFilter::getMatchString() +{ + const String* pStr = mrSharedString.getString(maItem.mnMatchStrId); + if (pStr) + return *pStr; + + return String(); +} + +double ScDPCacheTable::SingleFilter::getMatchValue() const +{ + return maItem.mfValue; +} + +bool ScDPCacheTable::SingleFilter::hasValue() const +{ + return maItem.mbHasValue; +} + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::GroupFilter::GroupFilter(ScSimpleSharedString& rSharedString) : + mrSharedString(rSharedString) +{ +} + +bool ScDPCacheTable::GroupFilter::match(const ScDPCacheCell& rCell) const +{ + vector<FilterItem>::const_iterator itrEnd = maItems.end(); + for (vector<FilterItem>::const_iterator itr = maItems.begin(); itr != itrEnd; ++itr) + { + bool bMatch = false; + if (rCell.mbNumeric) + bMatch = (itr->mfValue == rCell.mfValue); + else + bMatch = (itr->mnMatchStrId == rCell.mnStrId); + + if (bMatch) + return true; + } + return false; +} + +void ScDPCacheTable::GroupFilter::addMatchItem(const String& rStr, double fVal, bool bHasValue) +{ + sal_Int32 nStrId = mrSharedString.getStringId(rStr); + FilterItem aItem; + aItem.mnMatchStrId = nStrId; + aItem.mfValue = fVal; + aItem.mbHasValue = bHasValue; + maItems.push_back(aItem); +} + +size_t ScDPCacheTable::GroupFilter::getMatchItemCount() const +{ + return maItems.size(); +} + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::Criterion::Criterion() : + mnFieldIndex(-1), + mpFilter(static_cast<FilterBase*>(NULL)) +{ +} + +// ---------------------------------------------------------------------------- + +ScDPCacheTable::ScDPCacheTable(ScDPCollection* pCollection) : + mrSharedString(pCollection->GetSharedString()), + mpCollection(pCollection) +{ +} + +ScDPCacheTable::~ScDPCacheTable() +{ +} + +sal_Int32 ScDPCacheTable::getRowSize() const +{ + return maTable.size(); +} + +sal_Int32 ScDPCacheTable::getColSize() const +{ + return maTable.empty() ? 0 : maTable[0].size(); +} + +void ScDPCacheTable::fillTable(ScDocument* pDoc, const ScRange& rRange, const ScQueryParam& rQuery, BOOL* pSpecial, + bool bIgnoreEmptyRows) +{ + SCTAB nTab = rRange.aStart.Tab(); + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nColCount = rRange.aEnd.Col() - rRange.aStart.Col() + 1; + SCROW nRowCount = rRange.aEnd.Row() - rRange.aStart.Row() + 1; + + if (nRowCount <= 1 || nColCount <= 0) + return; + + maTable.clear(); + maTable.reserve(nRowCount); + maHeader.clear(); + maHeader.reserve(nColCount); + maRowsVisible.clear(); + maRowsVisible.reserve(nRowCount); + + // Header row + for (SCCOL nCol = 0; nCol < nColCount; ++nCol) + { + String aStr; + pDoc->GetString(nCol + nStartCol, nStartRow, nTab, aStr); + sal_Int32 nStrId = mrSharedString.insertString(aStr); + maHeader.push_back(nStrId); + } + + // Initialize field entries container. + maFieldEntries.clear(); + maFieldEntries.reserve(nColCount); + for (SCCOL nCol = 0; nCol < nColCount; ++nCol) + { + TypedScStrCollectionPtr p(new TypedScStrCollection); + maFieldEntries.push_back(p); + } + + vector<SCROW> aLastNonEmptyRows(nColCount, 0); + + // Data rows + for (SCROW nRow = 1; nRow < nRowCount; ++nRow) + { + if ( lcl_HasQueryEntry(rQuery) && !pDoc->ValidQuery(nRow + nStartRow, nTab, rQuery, pSpecial) ) + // filtered out by standard filter. + continue; + + if ( bIgnoreEmptyRows && + pDoc->IsBlockEmpty(nTab, nStartCol, nRow + nStartRow, + nStartCol + nColCount - 1, nRow + nStartRow) ) + // skip an empty row. + continue; + + // Insert a new row into cache table. + maRowsVisible.push_back(true); + maTable.push_back( vector<Cell>() ); + maTable.back().reserve(nColCount); + + for (SCCOL nCol = 0; nCol < nColCount; ++nCol) + { + maTable.back().push_back( ScDPCacheTable::Cell() ); + Cell& rCell = maTable.back().back(); + rCell.mnCategoryRef = maTable.size()-1; + + String aCellStr; + bool bReadCell = nRow == 0 || pDoc->HasData(nStartCol + nCol, nStartRow + nRow, nTab); + if (bReadCell) + { + aLastNonEmptyRows[nCol] = maTable.size()-1; + ScDPCacheCell aCell; + pDoc->GetString(nStartCol + nCol, nStartRow + nRow, nTab, aCellStr); + aCell.mnStrId = mrSharedString.insertString(aCellStr); + aCell.mnType = SC_VALTYPE_STRING; + aCell.mbNumeric = false; + ScAddress aPos(nStartCol + nCol, nStartRow + nRow, nTab); + getValueData(pDoc, aPos, aCell); + rCell.mpContent = mpCollection->getCacheCellFromPool(aCell); + } + else + rCell.mnCategoryRef = aLastNonEmptyRows[nCol]; + + TypedStrData* pNew; + if (rCell.mpContent && rCell.mpContent->mbNumeric) + pNew = new TypedStrData(aCellStr, rCell.mpContent->mfValue, SC_STRTYPE_VALUE); + else + pNew = new TypedStrData(aCellStr); + + if (!maFieldEntries[nCol]->Insert(pNew)) + delete pNew; + } + } +} + +void lcl_GetCellValue(const Reference<sdbc::XRow>& xRow, sal_Int32 nType, long nCol, + const Date& rNullDate, ScDPCacheCell& rCell, String& rStr, + ScSimpleSharedString& rSharedString) +{ + short nNumType = NUMBERFORMAT_NUMBER; + BOOL bEmptyFlag = FALSE; + try + { + rStr = xRow->getString(nCol); + rCell.mnStrId = rSharedString.getStringId(rStr); + rCell.mnType = SC_VALTYPE_STRING; + + switch (nType) + { + case sdbc::DataType::BIT: + case sdbc::DataType::BOOLEAN: + { + nNumType = NUMBERFORMAT_LOGICAL; + rCell.mfValue = xRow->getBoolean(nCol) ? 1 : 0; + bEmptyFlag = (rCell.mfValue == 0.0 && xRow->wasNull()); + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } + break; + + case sdbc::DataType::TINYINT: + case sdbc::DataType::SMALLINT: + case sdbc::DataType::INTEGER: + case sdbc::DataType::BIGINT: + case sdbc::DataType::FLOAT: + case sdbc::DataType::REAL: + case sdbc::DataType::DOUBLE: + case sdbc::DataType::NUMERIC: + case sdbc::DataType::DECIMAL: + { + //! do the conversion here? + rCell.mfValue = xRow->getDouble(nCol); + bEmptyFlag = (rCell.mfValue == 0.0 && xRow->wasNull()); + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } + break; + + case sdbc::DataType::CHAR: + case sdbc::DataType::VARCHAR: + case sdbc::DataType::LONGVARCHAR: + bEmptyFlag = (rStr.Len() == 0 && xRow->wasNull()); + break; + + case sdbc::DataType::DATE: + { + nNumType = NUMBERFORMAT_DATE; + + util::Date aDate = xRow->getDate(nCol); + rCell.mfValue = Date(aDate.Day, aDate.Month, aDate.Year) - rNullDate; + bEmptyFlag = xRow->wasNull(); + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } + break; + + case sdbc::DataType::TIME: + { + nNumType = NUMBERFORMAT_TIME; + + util::Time aTime = xRow->getTime(nCol); + rCell.mfValue = ( aTime.Hours * 3600 + aTime.Minutes * 60 + + aTime.Seconds + aTime.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; + bEmptyFlag = xRow->wasNull(); + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } + break; + + case sdbc::DataType::TIMESTAMP: + { + nNumType = NUMBERFORMAT_DATETIME; + + util::DateTime aStamp = xRow->getTimestamp(nCol); + rCell.mfValue = ( Date( aStamp.Day, aStamp.Month, aStamp.Year ) - rNullDate ) + + ( aStamp.Hours * 3600 + aStamp.Minutes * 60 + + aStamp.Seconds + aStamp.HundredthSeconds / 100.0 ) / D_TIMEFACTOR; + bEmptyFlag = xRow->wasNull(); + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } + break; + + case sdbc::DataType::SQLNULL: + case sdbc::DataType::BINARY: + case sdbc::DataType::VARBINARY: + case sdbc::DataType::LONGVARBINARY: + default: + break; + } + } + catch (uno::Exception&) + { + } +} + +void ScDPCacheTable::fillTable(const Reference<sdbc::XRowSet>& xRowSet, const Date& rNullDate) +{ + if (!xRowSet.is()) + // Dont' even waste time to go any further. + return; + + try + { + Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp(xRowSet, UNO_QUERY_THROW); + Reference<sdbc::XResultSetMetaData> xMeta = xMetaSupp->getMetaData(); + if (!xMeta.is()) + return; + + sal_Int32 nColCount = xMeta->getColumnCount(); + + // Get column titles and types. + vector<sal_Int32> aColTypes(nColCount); + maHeader.clear(); + maHeader.reserve(nColCount); + for (sal_Int32 nCol = 0; nCol < nColCount; ++nCol) + { + String aColTitle = xMeta->getColumnLabel(nCol+1); + aColTypes[nCol] = xMeta->getColumnType(nCol+1); + maHeader.push_back( mrSharedString.getStringId(aColTitle) ); + } + + // Initialize field entries container. + maFieldEntries.clear(); + maFieldEntries.reserve(nColCount); + for (SCCOL nCol = 0; nCol < nColCount; ++nCol) + { + TypedScStrCollectionPtr p(new TypedScStrCollection); + maFieldEntries.push_back(p); + } + + // Now get the data rows. + Reference<sdbc::XRow> xRow(xRowSet, UNO_QUERY_THROW); + xRowSet->first(); + maTable.clear(); + maRowsVisible.clear(); + do + { + maRowsVisible.push_back(true); + maTable.push_back( vector<Cell>() ); + maTable.back().reserve(nColCount); + for (sal_Int32 nCol = 0; nCol < nColCount; ++nCol) + { + maTable.back().push_back( Cell() ); + Cell& rCell = maTable.back().back(); + ScDPCacheCell aCellContent; + String aStr; + lcl_GetCellValue(xRow, aColTypes[nCol], nCol+1, rNullDate, aCellContent, aStr, mrSharedString); + rCell.mpContent = mpCollection->getCacheCellFromPool(aCellContent); + + TypedStrData* pNew; + if (rCell.mpContent->mbNumeric) + pNew = new TypedStrData(aStr, rCell.mpContent->mfValue, SC_STRTYPE_VALUE); + else + pNew = new TypedStrData(aStr); + + if (!maFieldEntries[nCol]->Insert(pNew)) + delete pNew; + } + } + while (xRowSet->next()); + + xRowSet->beforeFirst(); + } + catch (const Exception&) + { + } +} + +bool ScDPCacheTable::isRowActive(sal_Int32 nRow) const +{ + if (nRow < 0 || static_cast<size_t>(nRow) >= maRowsVisible.size()) + // row index out of bound + return false; + + return maRowsVisible[nRow]; +} + +void ScDPCacheTable::filterByPageDimension(const vector<Criterion>& rCriteria, const hash_set<sal_Int32>& rRepeatIfEmptyDims) +{ + sal_Int32 nRowSize = getRowSize(); + if (nRowSize != static_cast<sal_Int32>(maRowsVisible.size())) + { + // sizes of the two tables differ! + return; + } + + for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow) + maRowsVisible[nRow] = isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims); +} + +const ScDPCacheCell* ScDPCacheTable::getCell(SCCOL nCol, SCROW nRow, bool bRepeatIfEmpty) const +{ + if ( nRow >= static_cast<SCROW>(maTable.size()) ) + return NULL; + + const vector<Cell>& rRow = maTable[nRow]; + if ( nCol < 0 || static_cast<size_t>(nCol) >= rRow.size() ) + return NULL; + + const Cell& rCell = rRow[nCol]; + const ScDPCacheCell* pCell = rCell.mpContent; + if (bRepeatIfEmpty && !pCell) + pCell = getCell(nCol, rCell.mnCategoryRef, false); + + return pCell ? pCell : &EmptyCellContent; +} + +const String* ScDPCacheTable::getFieldName(sal_Int32 nIndex) const +{ + if (nIndex >= static_cast<sal_Int32>(maHeader.size())) + return NULL; + + return mrSharedString.getString(maHeader[nIndex]); +} + +const TypedScStrCollection& ScDPCacheTable::getFieldEntries(sal_Int32 nIndex) const +{ + if (nIndex < 0 || static_cast<size_t>(nIndex) >= maFieldEntries.size()) + { + // index out of bound. Hopefully this code will never be reached. + static const TypedScStrCollection emptyCollection; + return emptyCollection; + } + + return *maFieldEntries[nIndex].get(); +} + +void ScDPCacheTable::filterTable(const vector<Criterion>& rCriteria, Sequence< Sequence<Any> >& rTabData, + const hash_set<sal_Int32>& rRepeatIfEmptyDims) +{ + sal_Int32 nRowSize = getRowSize(); + sal_Int32 nColSize = getColSize(); + + if (!nRowSize) + // no data to filter. + return; + + // Row first, then column. + vector< Sequence<Any> > tableData; + tableData.reserve(nRowSize+1); + + // Header first. + Sequence<Any> headerRow(nColSize); + for (sal_Int32 nCol = 0; nCol < nColSize; ++nCol) + { + OUString str; + const String* pStr = mrSharedString.getString(maHeader[nCol]); + if (pStr) + str = *pStr; + + Any any; + any <<= str; + headerRow[nCol] = any; + } + tableData.push_back(headerRow); + + + for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow) + { + if (!maRowsVisible[nRow]) + // This row is filtered out. + continue; + + if (!isRowQualified(nRow, rCriteria, rRepeatIfEmptyDims)) + continue; + + // Insert this row into table. + + Sequence<Any> row(nColSize); + for (SCCOL nCol = 0; nCol < nColSize; ++nCol) + { + Any any; + bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(nCol) > 0; + const ScDPCacheCell* pCell = getCell(nCol, nRow, bRepeatIfEmpty); + if (!pCell) + { + // This should never happen, but in case this happens, just + // stick in an empty string. + OUString str; + any <<= str; + row[nCol] = any; + continue; + } + + if (pCell->mbNumeric) + any <<= pCell->mfValue; + else + { + OUString str; + const String* pStr = mrSharedString.getString(pCell->mnStrId); + if (pStr) + str = *pStr; + any <<= str; + } + row[nCol] = any; + } + tableData.push_back(row); + } + + // convert vector to Seqeunce + sal_Int32 nTabSize = static_cast<sal_Int32>(tableData.size()); + rTabData.realloc(nTabSize); + for (sal_Int32 i = 0; i < nTabSize; ++i) + rTabData[i] = tableData[i]; +} + +void ScDPCacheTable::clear() +{ + maTable.clear(); + maHeader.clear(); + maFieldEntries.clear(); + maRowsVisible.clear(); +} + +bool ScDPCacheTable::empty() const +{ + return maTable.empty(); +} + +bool ScDPCacheTable::isRowQualified(sal_Int32 nRow, const vector<Criterion>& rCriteria, + const hash_set<sal_Int32>& rRepeatIfEmptyDims) const +{ + sal_Int32 nColSize = getColSize(); + vector<Criterion>::const_iterator itrEnd = rCriteria.end(); + for (vector<Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr) + { + if (itr->mnFieldIndex >= nColSize) + // specified field is outside the source data columns. Don't + // use this criterion. + continue; + + // Check if the 'repeat if empty' flag is set for this field. + bool bRepeatIfEmpty = rRepeatIfEmptyDims.count(itr->mnFieldIndex) > 0; + const ScDPCacheCell* pCell = getCell(static_cast<SCCOL>(itr->mnFieldIndex), nRow, bRepeatIfEmpty); + if (!pCell) + // This should never happen, but just in case... + return false; + + if (!itr->mpFilter->match(*pCell)) + return false; + } + return true; +} + +void ScDPCacheTable::getValueData(ScDocument* pDoc, const ScAddress& rPos, ScDPCacheCell& rCell) +{ + ScBaseCell* pCell = pDoc->GetCell(rPos); + if (!pCell) + { + rCell.mnType = SC_VALTYPE_EMPTY; + return; + } + + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_NOTE) + { + // note cell + rCell.mnType = SC_VALTYPE_EMPTY; + return; + } + + if (eType == CELLTYPE_FORMULA && static_cast<ScFormulaCell*>(pCell)->GetErrCode()) + { + // formula cell with error + rCell.mnType = SC_VALTYPE_ERROR; + return; + } + + if ( pCell->HasValueData() ) + { + if (eType == CELLTYPE_VALUE) + // value cell + rCell.mfValue = static_cast<ScValueCell*>(pCell)->GetValue(); + else if (eType == CELLTYPE_FORMULA) + // formula cell + rCell.mfValue = static_cast<ScFormulaCell*>(pCell)->GetValue(); + + rCell.mbNumeric = true; + rCell.mnType = SC_VALTYPE_VALUE; + } +} + diff --git a/sc/source/core/data/dpdimsave.cxx b/sc/source/core/data/dpdimsave.cxx new file mode 100644 index 000000000000..7e1bc0389eaf --- /dev/null +++ b/sc/source/core/data/dpdimsave.cxx @@ -0,0 +1,587 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpdimsave.cxx,v $ + * $Revision: 1.7 $ + * + * 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 "dpdimsave.hxx" +#include "dpgroup.hxx" +#include "dpobject.hxx" +#include "document.hxx" + +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> + +#include <svtools/zforlist.hxx> +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <algorithm> + +// ============================================================================ + +ScDPSaveGroupItem::ScDPSaveGroupItem( const String& rName ) : + aGroupName( rName ) +{ +} + +ScDPSaveGroupItem::~ScDPSaveGroupItem() +{ +} + +void ScDPSaveGroupItem::AddElement( const String& rName ) +{ + aElements.push_back( rName ); +} + +void ScDPSaveGroupItem::AddElementsFromGroup( const ScDPSaveGroupItem& rGroup ) +{ + // add all elements of the other group (used for nested grouping) + + for ( std::vector<String>::const_iterator aIter(rGroup.aElements.begin()); + aIter != rGroup.aElements.end(); aIter++ ) + aElements.push_back( *aIter ); +} + +bool ScDPSaveGroupItem::RemoveElement( const String& rName ) +{ + for ( std::vector<String>::iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ ) + if ( *aIter == rName ) //! ignore case + { + aElements.erase( aIter ); // found -> remove + return true; // don't have to look further + } + + return false; // not found +} + +bool ScDPSaveGroupItem::IsEmpty() const +{ + return aElements.empty(); +} + +size_t ScDPSaveGroupItem::GetElementCount() const +{ + return aElements.size(); +} + +const String* ScDPSaveGroupItem::GetElementByIndex( size_t nIndex ) const +{ + return (nIndex < aElements.size()) ? &aElements[ nIndex ] : 0; +} + +void ScDPSaveGroupItem::Rename( const String& rNewName ) +{ + aGroupName = rNewName; +} + +void ScDPSaveGroupItem::RemoveElementsFromGroups( ScDPSaveGroupDimension& rDimension ) const +{ + // remove this group's elements from their groups in rDimension + // (rDimension must be a different dimension from the one which contains this) + + for ( std::vector<String>::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ ) + rDimension.RemoveFromGroups( *aIter ); +} + +void ScDPSaveGroupItem::AddToData( ScDPGroupDimension& rDataDim, SvNumberFormatter* pFormatter ) const +{ + ScDPGroupItem aGroup( aGroupName ); + ScDPItemData aData; + + for ( std::vector<String>::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ ) + { + sal_uInt32 nFormat = 0; //! ... + double fValue; + if ( pFormatter->IsNumberFormat( *aIter, nFormat, fValue ) ) + aData = ScDPItemData( *aIter, fValue, TRUE ); + else + aData.SetString( *aIter ); + + aGroup.AddElement( aData ); + //! for numeric data, look at source members? + } + + rDataDim.AddItem( aGroup ); +} + +// ============================================================================ + +ScDPSaveGroupDimension::ScDPSaveGroupDimension( const String& rSource, const String& rName ) : + aSourceDim( rSource ), + aGroupDimName( rName ), + nDatePart( 0 ) +{ +} + +ScDPSaveGroupDimension::ScDPSaveGroupDimension( const String& rSource, const String& rName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) : + aSourceDim( rSource ), + aGroupDimName( rName ), + aDateInfo( rDateInfo ), + nDatePart( nPart ) +{ +} + +ScDPSaveGroupDimension::~ScDPSaveGroupDimension() +{ +} + +void ScDPSaveGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) +{ + aDateInfo = rInfo; + nDatePart = nPart; +} + +void ScDPSaveGroupDimension::AddGroupItem( const ScDPSaveGroupItem& rItem ) +{ + aGroups.push_back( rItem ); +} + +String ScDPSaveGroupDimension::CreateGroupName( const String& rPrefix ) +{ + // create a name for a new group, using "Group1", "Group2" etc. (translated prefix in rPrefix) + + //! look in all dimensions, to avoid clashes with automatic groups (=name of base element)? + //! (only dimensions for the same base) + + sal_Int32 nAdd = 1; // first try is "Group1" + const sal_Int32 nMaxAdd = nAdd + aGroups.size(); // limit the loop + while ( nAdd <= nMaxAdd ) + { + String aGroupName( rPrefix ); + aGroupName.Append( String::CreateFromInt32( nAdd ) ); + bool bExists = false; + + // look for existing groups + for ( ScDPSaveGroupItemVec::const_iterator aIter(aGroups.begin()); + aIter != aGroups.end() && !bExists; aIter++ ) + if ( aIter->GetGroupName() == aGroupName ) //! ignore case + bExists = true; + + if ( !bExists ) + return aGroupName; // found a new name + + ++nAdd; // continue with higher number + } + + DBG_ERROR("CreateGroupName: no valid name found"); + return EMPTY_STRING; +} + +const ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroup( const String& rGroupName ) const +{ + return const_cast< ScDPSaveGroupDimension* >( this )->GetNamedGroupAcc( rGroupName ); +} + +ScDPSaveGroupItem* ScDPSaveGroupDimension::GetNamedGroupAcc( const String& rGroupName ) +{ + for ( ScDPSaveGroupItemVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + if ( aIter->GetGroupName() == rGroupName ) //! ignore case + return &*aIter; + + return NULL; // none found +} + +long ScDPSaveGroupDimension::GetGroupCount() const +{ + return aGroups.size(); +} + +const ScDPSaveGroupItem* ScDPSaveGroupDimension::GetGroupByIndex( long nIndex ) const +{ + return const_cast< ScDPSaveGroupDimension* >( this )->GetGroupAccByIndex( nIndex ); +} + +ScDPSaveGroupItem* ScDPSaveGroupDimension::GetGroupAccByIndex( long nIndex ) +{ + return &aGroups[nIndex]; +} + +void ScDPSaveGroupDimension::RemoveFromGroups( const String& rItemName ) +{ + // if the item is in any group, remove it from the group, + // also remove the group if it is empty afterwards + + for ( ScDPSaveGroupItemVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + if ( aIter->RemoveElement( rItemName ) ) + { + if ( aIter->IsEmpty() ) // removed last item from the group? + aGroups.erase( aIter ); // then remove the group + + return; // don't have to look further + } +} + +void ScDPSaveGroupDimension::RemoveGroup( const String& rGroupName ) +{ + for ( ScDPSaveGroupItemVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + if ( aIter->GetGroupName() == rGroupName ) //! ignore case + { + aGroups.erase( aIter ); + return; // don't have to look further + } +} + +bool ScDPSaveGroupDimension::IsEmpty() const +{ + return aGroups.empty(); +} + +bool ScDPSaveGroupDimension::HasOnlyHidden( const ScStrCollection& rVisible ) +{ + // check if there are only groups that don't appear in the list of visible names + + bool bAllHidden = true; + for ( ScDPSaveGroupItemVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end() && bAllHidden; aIter++ ) + { + StrData aSearch( aIter->GetGroupName() ); + USHORT nCollIndex; + if ( rVisible.Search( &aSearch, nCollIndex ) ) + bAllHidden = false; // found one that is visible + } + return bAllHidden; +} + +void ScDPSaveGroupDimension::Rename( const String& rNewName ) +{ + aGroupDimName = rNewName; +} + +void ScDPSaveGroupDimension::AddToData( ScDPGroupTableData& rData ) const +{ + long nSourceIndex = rData.GetDimensionIndex( aSourceDim ); + if ( nSourceIndex >= 0 ) + { + ScDPGroupDimension aDim( nSourceIndex, aGroupDimName ); + if ( nDatePart ) + { + // date grouping + + aDim.MakeDateHelper( aDateInfo, nDatePart ); + } + else + { + // normal (manual) grouping + + SvNumberFormatter* pFormatter = rData.GetDocument()->GetFormatTable(); + + for ( ScDPSaveGroupItemVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + aIter->AddToData( aDim, pFormatter ); + } + + rData.AddGroupDimension( aDim ); + } +} + +// ============================================================================ + +ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( const String& rName, const ScDPNumGroupInfo& rInfo ) : + aDimensionName( rName ), + aGroupInfo( rInfo ), + nDatePart( 0 ) +{ +} + +ScDPSaveNumGroupDimension::ScDPSaveNumGroupDimension( const String& rName, const ScDPNumGroupInfo& rDateInfo, sal_Int32 nPart ) : + aDimensionName( rName ), + aDateInfo( rDateInfo ), + nDatePart( nPart ) +{ +} + +ScDPSaveNumGroupDimension::~ScDPSaveNumGroupDimension() +{ +} + +void ScDPSaveNumGroupDimension::AddToData( ScDPGroupTableData& rData ) const +{ + long nSource = rData.GetDimensionIndex( aDimensionName ); + if ( nSource >= 0 ) + { + ScDPNumGroupDimension aDim( aGroupInfo ); // aGroupInfo: value grouping + if ( nDatePart ) + aDim.MakeDateHelper( aDateInfo, nDatePart ); // date grouping + + rData.SetNumGroupDimension( nSource, aDim ); + } +} + +void ScDPSaveNumGroupDimension::SetGroupInfo( const ScDPNumGroupInfo& rNew ) +{ + aGroupInfo = rNew; +} + +void ScDPSaveNumGroupDimension::SetDateInfo( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) +{ + aDateInfo = rInfo; + nDatePart = nPart; +} + +// ============================================================================ + +namespace { + +struct ScDPSaveGroupDimNameFunc +{ + String maDimName; + inline explicit ScDPSaveGroupDimNameFunc( const String& rDimName ) : maDimName( rDimName ) {} + inline bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetGroupDimName() == maDimName; } +}; + +struct ScDPSaveGroupSourceNameFunc +{ + String maSrcDimName; + inline explicit ScDPSaveGroupSourceNameFunc( const String& rSrcDimName ) : maSrcDimName( rSrcDimName ) {} + inline bool operator()( const ScDPSaveGroupDimension& rGroupDim ) const { return rGroupDim.GetSourceDimName() == maSrcDimName; } +}; + +} // namespace + +// ---------------------------------------------------------------------------- + +ScDPDimensionSaveData::ScDPDimensionSaveData() +{ +} + +ScDPDimensionSaveData::~ScDPDimensionSaveData() +{ +} + +bool ScDPDimensionSaveData::operator==( const ScDPDimensionSaveData& ) const +{ + return false; +} + +void ScDPDimensionSaveData::AddGroupDimension( const ScDPSaveGroupDimension& rGroupDim ) +{ + DBG_ASSERT( ::std::find_if( maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) ) == maGroupDims.end(), + "ScDPDimensionSaveData::AddGroupDimension - group dimension exists already" ); + // ReplaceGroupDimension() adds new or replaces existing + ReplaceGroupDimension( rGroupDim ); +} + +void ScDPDimensionSaveData::ReplaceGroupDimension( const ScDPSaveGroupDimension& rGroupDim ) +{ + ScDPSaveGroupDimVec::iterator aIt = ::std::find_if( + maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDim.GetGroupDimName() ) ); + if( aIt == maGroupDims.end() ) + maGroupDims.push_back( rGroupDim ); + else + *aIt = rGroupDim; +} + +void ScDPDimensionSaveData::RemoveGroupDimension( const String& rGroupDimName ) +{ + ScDPSaveGroupDimVec::iterator aIt = ::std::find_if( + maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) ); + if( aIt != maGroupDims.end() ) + maGroupDims.erase( aIt ); +} + +void ScDPDimensionSaveData::AddNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim ) +{ + DBG_ASSERT( maNumGroupDims.count( rGroupDim.GetDimensionName() ) == 0, + "ScDPDimensionSaveData::AddNumGroupDimension - numeric group dimension exists already" ); + // ReplaceNumGroupDimension() adds new or replaces existing + ReplaceNumGroupDimension( rGroupDim ); +} + +void ScDPDimensionSaveData::ReplaceNumGroupDimension( const ScDPSaveNumGroupDimension& rGroupDim ) +{ + ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDim.GetDimensionName() ); + if( aIt == maNumGroupDims.end() ) + maNumGroupDims.insert( ScDPSaveNumGroupDimMap::value_type( rGroupDim.GetDimensionName(), rGroupDim ) ); + else + aIt->second = rGroupDim; +} + +void ScDPDimensionSaveData::RemoveNumGroupDimension( const String& rGroupDimName ) +{ + maNumGroupDims.erase( rGroupDimName ); +} + +void ScDPDimensionSaveData::WriteToData( ScDPGroupTableData& rData ) const +{ + // rData is assumed to be empty + // AddToData also handles date grouping + + for( ScDPSaveGroupDimVec::const_iterator aIt = maGroupDims.begin(), aEnd = maGroupDims.end(); aIt != aEnd; ++aIt ) + aIt->AddToData( rData ); + + for( ScDPSaveNumGroupDimMap::const_iterator aIt = maNumGroupDims.begin(), aEnd = maNumGroupDims.end(); aIt != aEnd; ++aIt ) + aIt->second.AddToData( rData ); +} + +const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimForBase( const String& rBaseDimName ) const +{ + return const_cast< ScDPDimensionSaveData* >( this )->GetGroupDimAccForBase( rBaseDimName ); +} + +const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDim( const String& rGroupDimName ) const +{ + return const_cast< ScDPDimensionSaveData* >( this )->GetNamedGroupDimAcc( rGroupDimName ); +} + +const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDim( const String& rBaseDimName ) const +{ + return const_cast< ScDPDimensionSaveData* >( this )->GetFirstNamedGroupDimAcc( rBaseDimName ); +} + +const ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDim( const String& rGroupDimName ) const +{ + return const_cast< ScDPDimensionSaveData* >( this )->GetNextNamedGroupDimAcc( rGroupDimName ); +} + +const ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDim( const String& rGroupDimName ) const +{ + return const_cast< ScDPDimensionSaveData* >( this )->GetNumGroupDimAcc( rGroupDimName ); +} + +ScDPSaveGroupDimension* ScDPDimensionSaveData::GetGroupDimAccForBase( const String& rBaseDimName ) +{ + ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDimAcc( rBaseDimName ); + return pGroupDim ? pGroupDim : GetNextNamedGroupDimAcc( rBaseDimName ); +} + +ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNamedGroupDimAcc( const String& rGroupDimName ) +{ + ScDPSaveGroupDimVec::iterator aIt = ::std::find_if( + maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) ); + return (aIt == maGroupDims.end()) ? 0 : &*aIt; +} + +ScDPSaveGroupDimension* ScDPDimensionSaveData::GetFirstNamedGroupDimAcc( const String& rBaseDimName ) +{ + ScDPSaveGroupDimVec::iterator aIt = ::std::find_if( + maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupSourceNameFunc( rBaseDimName ) ); + return (aIt == maGroupDims.end()) ? 0 : &*aIt; +} + +ScDPSaveGroupDimension* ScDPDimensionSaveData::GetNextNamedGroupDimAcc( const String& rGroupDimName ) +{ + // find the group dimension with the passed name + ScDPSaveGroupDimVec::iterator aIt = ::std::find_if( + maGroupDims.begin(), maGroupDims.end(), ScDPSaveGroupDimNameFunc( rGroupDimName ) ); + // find next group dimension based on the same source dimension name + if( aIt != maGroupDims.end() ) + aIt = ::std::find_if( aIt + 1, maGroupDims.end(), ScDPSaveGroupSourceNameFunc( aIt->GetSourceDimName() ) ); + return (aIt == maGroupDims.end()) ? 0 : &*aIt; +} + +ScDPSaveNumGroupDimension* ScDPDimensionSaveData::GetNumGroupDimAcc( const String& rGroupDimName ) +{ + ScDPSaveNumGroupDimMap::iterator aIt = maNumGroupDims.find( rGroupDimName ); + return (aIt == maNumGroupDims.end()) ? 0 : &aIt->second; +} + +bool ScDPDimensionSaveData::HasGroupDimensions() const +{ + return !maGroupDims.empty() || !maNumGroupDims.empty(); +} + +sal_Int32 ScDPDimensionSaveData::CollectDateParts( const String& rBaseDimName ) const +{ + sal_Int32 nParts = 0; + // start with part of numeric group + if( const ScDPSaveNumGroupDimension* pNumDim = GetNumGroupDim( rBaseDimName ) ) + nParts |= pNumDim->GetDatePart(); + // collect parts from all matching group dimensions + for( const ScDPSaveGroupDimension* pGroupDim = GetFirstNamedGroupDim( rBaseDimName ); pGroupDim; pGroupDim = GetNextNamedGroupDim( pGroupDim->GetGroupDimName() ) ) + nParts |= pGroupDim->GetDatePart(); + + return nParts; +} + +String ScDPDimensionSaveData::CreateGroupDimName( const String& rSourceName, + const ScDPObject& rObject, bool bAllowSource, + const std::vector<String>* pDeletedNames ) +{ + // create a name for the new dimension by appending a number to the original + // dimension's name + + bool bUseSource = bAllowSource; // if set, try the unchanged original name first + + sal_Int32 nAdd = 2; // first try is "Name2" + const sal_Int32 nMaxAdd = 1000; // limit the loop + while ( nAdd <= nMaxAdd ) + { + String aDimName( rSourceName ); + if ( !bUseSource ) + aDimName.Append( String::CreateFromInt32( nAdd ) ); + bool bExists = false; + + // look for existing group dimensions + for( ScDPSaveGroupDimVec::const_iterator aIt = maGroupDims.begin(), aEnd = maGroupDims.end(); (aIt != aEnd) && !bExists; ++aIt ) + if( aIt->GetGroupDimName() == aDimName ) //! ignore case + bExists = true; + + // look for base dimensions that happen to have that name + if ( !bExists && rObject.IsDimNameInUse( aDimName ) ) + { + if ( pDeletedNames && + std::find( pDeletedNames->begin(), pDeletedNames->end(), aDimName ) != pDeletedNames->end() ) + { + // allow the name anyway if the name is in pDeletedNames + } + else + bExists = true; + } + + if ( !bExists ) + return aDimName; // found a new name + + if ( bUseSource ) + bUseSource = false; + else + ++nAdd; // continue with higher number + } + DBG_ERROR("CreateGroupDimName: no valid name found"); + return EMPTY_STRING; +} + +String ScDPDimensionSaveData::CreateDateGroupDimName( sal_Int32 nDatePart, const ScDPObject& rObject, bool bAllowSource, const ::std::vector< String >* pDeletedNames ) +{ + using namespace ::com::sun::star::sheet::DataPilotFieldGroupBy; + String aPartName; + switch( nDatePart ) + { + //! use translated strings from globstr.src + case SECONDS: aPartName = String::CreateFromAscii( "Seconds" ); break; + case MINUTES: aPartName = String::CreateFromAscii( "Minutes" ); break; + case HOURS: aPartName = String::CreateFromAscii( "Hours" ); break; + case DAYS: aPartName = String::CreateFromAscii( "Days" ); break; + case MONTHS: aPartName = String::CreateFromAscii( "Months" ); break; + case QUARTERS: aPartName = String::CreateFromAscii( "Quarters" ); break; + case YEARS: aPartName = String::CreateFromAscii( "Years" ); break; + } + DBG_ASSERT( aPartName.Len() > 0, "ScDPDimensionSaveData::CreateDateGroupDimName - invalid date part" ); + return CreateGroupDimName( aPartName, rObject, bAllowSource, pDeletedNames ); +} + +// ============================================================================ + diff --git a/sc/source/core/data/dpgroup.cxx b/sc/source/core/data/dpgroup.cxx new file mode 100644 index 000000000000..a2d21079826d --- /dev/null +++ b/sc/source/core/data/dpgroup.cxx @@ -0,0 +1,1534 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpgroup.cxx,v $ + * $Revision: 1.11 $ + * + * 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 <com/sun/star/i18n/CalendarDisplayIndex.hpp> + +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <unotools/localedatawrapper.hxx> +#include <svtools/zforlist.hxx> + +#include "dpgroup.hxx" +#include "collect.hxx" +#include "global.hxx" +#include "document.hxx" +#include "dpcachetable.hxx" +#include "dptabsrc.hxx" +#include "dptabres.hxx" +#include "dpobject.hxx" + +#include <com/sun/star/sheet/DataPilotFieldGroupBy.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> + +#include <vector> +#include <hash_set> +#include <hash_map> + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::UNO_QUERY_THROW; +using ::rtl::OUString; +using ::rtl::OUStringHash; + +using ::std::vector; +using ::std::hash_set; +using ::std::hash_map; + +#define D_TIMEFACTOR 86400.0 + +const USHORT SC_DP_LEAPYEAR = 1648; // arbitrary leap year for date calculations + +// part values for the extra "<" and ">" entries (same for all parts) +const sal_Int32 SC_DP_DATE_FIRST = -1; +const sal_Int32 SC_DP_DATE_LAST = 10000; + +// ============================================================================ + +class ScDPGroupDateFilter : public ScDPCacheTable::FilterBase +{ +public: + ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart, + const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo); + + virtual bool match(const ScDPCacheCell &rCell) const; + +private: + ScDPGroupDateFilter(); // disabled + + const Date* mpNullDate; + const ScDPNumGroupInfo* mpNumInfo; + double mfMatchValue; + sal_Int32 mnDatePart; +}; + +// ---------------------------------------------------------------------------- + +ScDPGroupDateFilter::ScDPGroupDateFilter(double fMatchValue, sal_Int32 nDatePart, + const Date* pNullDate, const ScDPNumGroupInfo* pNumInfo) : + mpNullDate(pNullDate), + mpNumInfo(pNumInfo), + mfMatchValue(fMatchValue), + mnDatePart(nDatePart) +{ +// fprintf(stdout, "ScDPCacheTable:DateGroupFilter::DateGroupFilter: match value = %g; date part = %ld\n", +// mfMatchValue, mnDatePart); +} + +bool ScDPGroupDateFilter::match(const ScDPCacheCell& rCell) const +{ + using namespace ::com::sun::star::sheet; + using ::rtl::math::approxFloor; + using ::rtl::math::approxEqual; + + if (!rCell.mbNumeric) + return false; + + if (!mpNumInfo) + return false; + + // Start and end dates are inclusive. (An end date without a time value + // is included, while an end date with a time value is not.) + + if ( rCell.mfValue < mpNumInfo->Start && !approxEqual(rCell.mfValue, mpNumInfo->Start) ) + return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_FIRST; + + if ( rCell.mfValue > mpNumInfo->End && !approxEqual(rCell.mfValue, mpNumInfo->End) ) + return static_cast<sal_Int32>(mfMatchValue) == SC_DP_DATE_LAST; + + if (mnDatePart == DataPilotFieldGroupBy::HOURS || mnDatePart == DataPilotFieldGroupBy::MINUTES || + mnDatePart == DataPilotFieldGroupBy::SECONDS) + { + // handle time + // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded) + + double time = rCell.mfValue - approxFloor(rCell.mfValue); + long seconds = static_cast<long>(approxFloor(time*D_TIMEFACTOR + 0.5)); + + switch (mnDatePart) + { + case DataPilotFieldGroupBy::HOURS: + { + sal_Int32 hrs = seconds / 3600; + sal_Int32 matchHrs = static_cast<sal_Int32>(mfMatchValue); + return hrs == matchHrs; + } + case DataPilotFieldGroupBy::MINUTES: + { + sal_Int32 minutes = (seconds % 3600) / 60; + sal_Int32 matchMinutes = static_cast<sal_Int32>(mfMatchValue); + return minutes == matchMinutes; + } + case DataPilotFieldGroupBy::SECONDS: + { + sal_Int32 sec = seconds % 60; + sal_Int32 matchSec = static_cast<sal_Int32>(mfMatchValue); + return sec == matchSec; + } + default: + DBG_ERROR("invalid time part"); + } + return false; + } + + Date date = *mpNullDate + static_cast<long>(approxFloor(rCell.mfValue)); + switch (mnDatePart) + { + case DataPilotFieldGroupBy::YEARS: + { + sal_Int32 year = static_cast<sal_Int32>(date.GetYear()); + sal_Int32 matchYear = static_cast<sal_Int32>(mfMatchValue); + return year == matchYear; + } + case DataPilotFieldGroupBy::QUARTERS: + { + sal_Int32 qtr = 1 + (static_cast<sal_Int32>(date.GetMonth()) - 1) / 3; + sal_Int32 matchQtr = static_cast<sal_Int32>(mfMatchValue); + return qtr == matchQtr; + } + case DataPilotFieldGroupBy::MONTHS: + { + sal_Int32 month = static_cast<sal_Int32>(date.GetMonth()); + sal_Int32 matchMonth = static_cast<sal_Int32>(mfMatchValue); + return month == matchMonth; + } + case DataPilotFieldGroupBy::DAYS: + { + Date yearStart(1, 1, date.GetYear()); + sal_Int32 days = (date - yearStart) + 1; // Jan 01 has value 1 + if (days >= 60 && !date.IsLeapYear()) + { + // This is not a leap year. Adjust the value accordingly. + ++days; + } + sal_Int32 matchDays = static_cast<sal_Int32>(mfMatchValue); + return days == matchDays; + } + default: + DBG_ERROR("invalid date part"); + } + + return false; +} + +// ============================================================================ + +void lcl_AppendDateStr( rtl::OUStringBuffer& rBuffer, double fValue, SvNumberFormatter* pFormatter ) +{ + ULONG nFormat = pFormatter->GetStandardFormat( NUMBERFORMAT_DATE, ScGlobal::eLnge ); + String aString; + pFormatter->GetInputLineString( fValue, nFormat, aString ); + rBuffer.append( aString ); +} + +// ----------------------------------------------------------------------- + +ScDPDateGroupHelper::ScDPDateGroupHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) : + aNumInfo( rInfo ), + nDatePart( nPart ) +{ +} + +ScDPDateGroupHelper::~ScDPDateGroupHelper() +{ +} + +String lcl_GetTwoDigitString( sal_Int32 nValue ) +{ + String aRet = String::CreateFromInt32( nValue ); + if ( aRet.Len() < 2 ) + aRet.Insert( (sal_Unicode)'0', 0 ); + return aRet; +} + +String lcl_GetDateGroupName( sal_Int32 nDatePart, sal_Int32 nValue, SvNumberFormatter* pFormatter ) +{ + String aRet; + switch ( nDatePart ) + { + case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS: + aRet = String::CreateFromInt32( nValue ); + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS: + aRet = ScGlobal::pLocaleData->getQuarterAbbreviation( (sal_Int16)(nValue - 1) ); // nValue is 1-based + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS: + //! cache getMonths() result? + aRet = ScGlobal::GetCalendar()->getDisplayName( + ::com::sun::star::i18n::CalendarDisplayIndex::MONTH, + sal_Int16(nValue-1), 0 ); // 0-based, get short name + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS: + { + Date aDate( 1, 1, SC_DP_LEAPYEAR ); + aDate += ( nValue - 1 ); // nValue is 1-based + Date aNullDate = *(pFormatter->GetNullDate()); + long nDays = aDate - aNullDate; + + ULONG nFormat = pFormatter->GetFormatIndex( NF_DATE_SYS_DDMMM, ScGlobal::eLnge ); + Color* pColor; + pFormatter->GetOutputString( nDays, nFormat, aRet, &pColor ); + } + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS: + //! allow am/pm format? + aRet = lcl_GetTwoDigitString( nValue ); + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES: + case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS: + aRet = ScGlobal::pLocaleData->getTimeSep(); + aRet.Append( lcl_GetTwoDigitString( nValue ) ); + break; + default: + DBG_ERROR("invalid date part"); + } + return aRet; +} + +sal_Int32 lcl_GetDatePartValue( double fValue, sal_Int32 nDatePart, SvNumberFormatter* pFormatter, + const ScDPNumGroupInfo* pNumInfo ) +{ + // Start and end are inclusive + // (End date without a time value is included, with a time value it's not) + + if ( pNumInfo ) + { + if ( fValue < pNumInfo->Start && !rtl::math::approxEqual( fValue, pNumInfo->Start ) ) + return SC_DP_DATE_FIRST; + if ( fValue > pNumInfo->End && !rtl::math::approxEqual( fValue, pNumInfo->End ) ) + return SC_DP_DATE_LAST; + } + + sal_Int32 nResult = 0; + + if ( nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::HOURS || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES || nDatePart == com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS ) + { + // handle time + // (as in the cell functions, ScInterpreter::ScGetHour etc.: seconds are rounded) + + double fTime = fValue - ::rtl::math::approxFloor(fValue); + long nSeconds = (long)::rtl::math::approxFloor(fTime*D_TIMEFACTOR+0.5); + + switch ( nDatePart ) + { + case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS: + nResult = nSeconds / 3600; + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES: + nResult = ( nSeconds % 3600 ) / 60; + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS: + nResult = nSeconds % 60; + break; + } + } + else + { + Date aDate = *(pFormatter->GetNullDate()); + aDate += (long)::rtl::math::approxFloor( fValue ); + + switch ( nDatePart ) + { + case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS: + nResult = aDate.GetYear(); + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS: + nResult = 1 + ( aDate.GetMonth() - 1 ) / 3; // 1..4 + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS: + nResult = aDate.GetMonth(); // 1..12 + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS: + { + Date aYearStart( 1, 1, aDate.GetYear() ); + nResult = ( aDate - aYearStart ) + 1; // Jan 01 has value 1 + if ( nResult >= 60 && !aDate.IsLeapYear() ) + { + // days are counted from 1 to 366 - if not from a leap year, adjust + ++nResult; + } + } + break; + default: + DBG_ERROR("invalid date part"); + } + } + + return nResult; +} + +BOOL lcl_DateContained( sal_Int32 nGroupPart, const ScDPItemData& rGroupData, + sal_Int32 nBasePart, const ScDPItemData& rBaseData ) +{ + if ( !rGroupData.bHasValue || !rBaseData.bHasValue ) + { + // non-numeric entries involved: only match equal entries + return rGroupData.IsCaseInsEqual( rBaseData ); + } + + // no approxFloor needed, values were created from integers + sal_Int32 nGroupValue = (sal_Int32) rGroupData.fValue; + sal_Int32 nBaseValue = (sal_Int32) rBaseData.fValue; + if ( nBasePart > nGroupPart ) + { + // switch, so the base part is the smaller (inner) part + + ::std::swap( nGroupPart, nBasePart ); + ::std::swap( nGroupValue, nBaseValue ); + } + + if ( nGroupValue == SC_DP_DATE_FIRST || nGroupValue == SC_DP_DATE_LAST || + nBaseValue == SC_DP_DATE_FIRST || nBaseValue == SC_DP_DATE_LAST ) + { + // first/last entry matches only itself + return ( nGroupValue == nBaseValue ); + } + + BOOL bContained = TRUE; + switch ( nBasePart ) // inner part + { + case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS: + // a month is only contained in its quarter + if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS ) + { + // months and quarters are both 1-based + bContained = ( nGroupValue - 1 == ( nBaseValue - 1 ) / 3 ); + } + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS: + // a day is only contained in its quarter or month + if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS || nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS ) + { + Date aDate( 1, 1, SC_DP_LEAPYEAR ); + aDate += ( nBaseValue - 1 ); // days are 1-based + sal_Int32 nCompare = aDate.GetMonth(); + if ( nGroupPart == com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS ) + nCompare = ( ( nCompare - 1 ) / 3 ) + 1; // get quarter from date + + bContained = ( nGroupValue == nCompare ); + } + break; + + // other parts: everything is contained + } + + return bContained; +} + +String lcl_GetSpecialDateName( double fValue, bool bFirst, SvNumberFormatter* pFormatter ) +{ + rtl::OUStringBuffer aBuffer; + aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' )); + lcl_AppendDateStr( aBuffer, fValue, pFormatter ); + return aBuffer.makeStringAndClear(); +} + +void ScDPDateGroupHelper::FillColumnEntries( TypedScStrCollection& rEntries, const TypedScStrCollection& rOriginal, + SvNumberFormatter* pFormatter ) const +{ + // auto min/max is only used for "Years" part, but the loop is always needed + double fSourceMin = 0.0; + double fSourceMax = 0.0; + bool bFirst = true; + + USHORT nOriginalCount = rOriginal.GetCount(); + for (USHORT nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++) + { + const TypedStrData& rStrData = *rOriginal[nOriginalPos]; + if ( rStrData.IsStrData() ) + { + // string data: just copy + TypedStrData* pNew = new TypedStrData( rStrData ); + if ( !rEntries.Insert( pNew ) ) + delete pNew; + } + else + { + double fSourceValue = rStrData.GetValue(); + if ( bFirst ) + { + fSourceMin = fSourceMax = fSourceValue; + bFirst = false; + } + else + { + if ( fSourceValue < fSourceMin ) + fSourceMin = fSourceValue; + if ( fSourceValue > fSourceMax ) + fSourceMax = fSourceValue; + } + } + } + + // For the start/end values, use the same date rounding as in ScDPNumGroupDimension::GetNumEntries + // (but not for the list of available years): + if ( aNumInfo.AutoStart ) + const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.Start = rtl::math::approxFloor( fSourceMin ); + if ( aNumInfo.AutoEnd ) + const_cast<ScDPDateGroupHelper*>(this)->aNumInfo.End = rtl::math::approxFloor( fSourceMax ) + 1; + + //! if not automatic, limit fSourceMin/fSourceMax for list of year values? + + long nStart = 0; + long nEnd = 0; // including + + switch ( nDatePart ) + { + case com::sun::star::sheet::DataPilotFieldGroupBy::YEARS: + nStart = lcl_GetDatePartValue( fSourceMin, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL ); + nEnd = lcl_GetDatePartValue( fSourceMax, com::sun::star::sheet::DataPilotFieldGroupBy::YEARS, pFormatter, NULL ); + break; + case com::sun::star::sheet::DataPilotFieldGroupBy::QUARTERS: nStart = 1; nEnd = 4; break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MONTHS: nStart = 1; nEnd = 12; break; + case com::sun::star::sheet::DataPilotFieldGroupBy::DAYS: nStart = 1; nEnd = 366; break; + case com::sun::star::sheet::DataPilotFieldGroupBy::HOURS: nStart = 0; nEnd = 23; break; + case com::sun::star::sheet::DataPilotFieldGroupBy::MINUTES: nStart = 0; nEnd = 59; break; + case com::sun::star::sheet::DataPilotFieldGroupBy::SECONDS: nStart = 0; nEnd = 59; break; + default: + DBG_ERROR("invalid date part"); + } + + for ( sal_Int32 nValue = nStart; nValue <= nEnd; nValue++ ) + { + String aName = lcl_GetDateGroupName( nDatePart, nValue, pFormatter ); + TypedStrData* pNew = new TypedStrData( aName, nValue, SC_STRTYPE_VALUE ); + if ( !rEntries.Insert( pNew ) ) + delete pNew; + } + + // add first/last entry (min/max) + + String aFirstName = lcl_GetSpecialDateName( aNumInfo.Start, true, pFormatter ); + TypedStrData* pFirstEntry = new TypedStrData( aFirstName, SC_DP_DATE_FIRST, SC_STRTYPE_VALUE ); + if ( !rEntries.Insert( pFirstEntry ) ) + delete pFirstEntry; + + String aLastName = lcl_GetSpecialDateName( aNumInfo.End, false, pFormatter ); + TypedStrData* pLastEntry = new TypedStrData( aLastName, SC_DP_DATE_LAST, SC_STRTYPE_VALUE ); + if ( !rEntries.Insert( pLastEntry ) ) + delete pLastEntry; +} + +// ----------------------------------------------------------------------- + +ScDPGroupItem::ScDPGroupItem( const ScDPItemData& rName ) : + aGroupName( rName ) +{ +} + +ScDPGroupItem::~ScDPGroupItem() +{ +} + +void ScDPGroupItem::AddElement( const ScDPItemData& rName ) +{ + aElements.push_back( rName ); +} + +bool ScDPGroupItem::HasElement( const ScDPItemData& rData ) const +{ + for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ ) + if ( aIter->IsCaseInsEqual( rData ) ) + return true; + + return false; +} + +bool ScDPGroupItem::HasCommonElement( const ScDPGroupItem& rOther ) const +{ + for ( ScDPItemDataVec::const_iterator aIter(aElements.begin()); aIter != aElements.end(); aIter++ ) + if ( rOther.HasElement( *aIter ) ) + return true; + + return false; +} + +void ScDPGroupItem::FillGroupFilter( ScDPCacheTable::GroupFilter& rFilter ) const +{ + ScDPItemDataVec::const_iterator itrEnd = aElements.end(); + for (ScDPItemDataVec::const_iterator itr = aElements.begin(); itr != itrEnd; ++itr) + rFilter.addMatchItem(itr->aString, itr->fValue, itr->bHasValue); +} + +// ----------------------------------------------------------------------- + +ScDPGroupDimension::ScDPGroupDimension( long nSource, const String& rNewName ) : + nSourceDim( nSource ), + nGroupDim( -1 ), + aGroupName( rNewName ), + pDateHelper( NULL ), + pCollection( NULL ) +{ +} + +ScDPGroupDimension::~ScDPGroupDimension() +{ + delete pDateHelper; + delete pCollection; +} + +ScDPGroupDimension::ScDPGroupDimension( const ScDPGroupDimension& rOther ) : + nSourceDim( rOther.nSourceDim ), + nGroupDim( rOther.nGroupDim ), + aGroupName( rOther.aGroupName ), + pDateHelper( NULL ), + aItems( rOther.aItems ), + pCollection( NULL ) // collection isn't copied - allocated on demand +{ + if ( rOther.pDateHelper ) + pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper ); +} + +ScDPGroupDimension& ScDPGroupDimension::operator=( const ScDPGroupDimension& rOther ) +{ + nSourceDim = rOther.nSourceDim; + nGroupDim = rOther.nGroupDim; + aGroupName = rOther.aGroupName; + aItems = rOther.aItems; + + delete pDateHelper; + if ( rOther.pDateHelper ) + pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper ); + else + pDateHelper = NULL; + + delete pCollection; // collection isn't copied - allocated on demand + pCollection = NULL; + return *this; +} + +void ScDPGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) +{ + delete pDateHelper; + pDateHelper = new ScDPDateGroupHelper( rInfo, nPart ); +} + +void ScDPGroupDimension::AddItem( const ScDPGroupItem& rItem ) +{ + aItems.push_back( rItem ); +} + +void ScDPGroupDimension::SetGroupDim( long nDim ) +{ + nGroupDim = nDim; +} + +const TypedScStrCollection& ScDPGroupDimension::GetColumnEntries( + const TypedScStrCollection& rOriginal, ScDocument* pDoc ) const +{ + if ( !pCollection ) + { + pCollection = new TypedScStrCollection(); + if ( pDateHelper ) + pDateHelper->FillColumnEntries( *pCollection, rOriginal, pDoc->GetFormatTable() ); + else + { + long nCount = aItems.size(); + for (long i=0; i<nCount; i++) + { + //! numeric entries? + TypedStrData* pNew = new TypedStrData( aItems[i].GetName().aString ); + if ( !pCollection->Insert( pNew ) ) + delete pNew; + } + + USHORT nOriginalCount = rOriginal.GetCount(); + for (USHORT nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++) + { + const TypedStrData& rStrData = *rOriginal[nOriginalPos]; + ScDPItemData aItemData( rStrData.GetString(), rStrData.GetValue(), !rStrData.IsStrData() ); + if ( !GetGroupForData( aItemData ) ) + { + // not in any group -> add as its own group + TypedStrData* pNew = new TypedStrData( rStrData ); + if ( !pCollection->Insert( pNew ) ) + delete pNew; + } + } + } + } + return *pCollection; +} + +const ScDPGroupItem* ScDPGroupDimension::GetGroupForData( const ScDPItemData& rData ) const +{ + for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ ) + if ( aIter->HasElement( rData ) ) + return &*aIter; + + return NULL; +} + +const ScDPGroupItem* ScDPGroupDimension::GetGroupForName( const ScDPItemData& rName ) const +{ + for ( ScDPGroupItemVec::const_iterator aIter(aItems.begin()); aIter != aItems.end(); aIter++ ) + if ( aIter->GetName().IsCaseInsEqual( rName ) ) + return &*aIter; + + return NULL; +} + +const ScDPGroupItem* ScDPGroupDimension::GetGroupByIndex( size_t nIndex ) const +{ + if (nIndex >= aItems.size()) + return NULL; + + return &aItems[nIndex]; +} + +void ScDPGroupDimension::DisposeData() +{ + delete pCollection; + pCollection = NULL; +} + +// ----------------------------------------------------------------------- + +ScDPNumGroupDimension::ScDPNumGroupDimension() : + pDateHelper( NULL ), + pCollection( NULL ), + bHasNonInteger( false ), + cDecSeparator( 0 ) +{ +} + +ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupInfo& rInfo ) : + aGroupInfo( rInfo ), + pDateHelper( NULL ), + pCollection( NULL ), + bHasNonInteger( false ), + cDecSeparator( 0 ) +{ +} + +ScDPNumGroupDimension::ScDPNumGroupDimension( const ScDPNumGroupDimension& rOther ) : + aGroupInfo( rOther.aGroupInfo ), + pDateHelper( NULL ), + pCollection( NULL ), // collection isn't copied - allocated on demand + bHasNonInteger( false ), + cDecSeparator( 0 ) +{ + if ( rOther.pDateHelper ) + pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper ); +} + +ScDPNumGroupDimension& ScDPNumGroupDimension::operator=( const ScDPNumGroupDimension& rOther ) +{ + aGroupInfo = rOther.aGroupInfo; + + delete pDateHelper; + if ( rOther.pDateHelper ) + pDateHelper = new ScDPDateGroupHelper( *rOther.pDateHelper ); + else + pDateHelper = NULL; + + delete pCollection; // collection isn't copied - allocated on demand + pCollection = NULL; + bHasNonInteger = false; + return *this; +} + +void ScDPNumGroupDimension::DisposeData() +{ + delete pCollection; + pCollection = NULL; + bHasNonInteger = false; +} + +ScDPNumGroupDimension::~ScDPNumGroupDimension() +{ + delete pDateHelper; + delete pCollection; +} + +void ScDPNumGroupDimension::MakeDateHelper( const ScDPNumGroupInfo& rInfo, sal_Int32 nPart ) +{ + delete pDateHelper; + pDateHelper = new ScDPDateGroupHelper( rInfo, nPart ); + + aGroupInfo.Enable = sal_True; //! or query both? +} + +String lcl_GetNumGroupName( double fStartValue, const ScDPNumGroupInfo& rInfo, + bool bHasNonInteger, sal_Unicode cDecSeparator, SvNumberFormatter* pFormatter ) +{ + DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" ); + + double fStep = rInfo.Step; + double fEndValue = fStartValue + fStep; + if ( !bHasNonInteger && ( rInfo.DateValues || !rtl::math::approxEqual( fEndValue, rInfo.End ) ) ) + { + // The second number of the group label is + // (first number + size - 1) if there are only integer numbers, + // (first number + size) if any non-integer numbers are involved. + // Exception: The last group (containing the end value) is always + // shown as including the end value (but not for dates). + + fEndValue -= 1.0; + } + + if ( fEndValue > rInfo.End && !rInfo.AutoEnd ) + { + // limit the last group to the end value + + fEndValue = rInfo.End; + } + + rtl::OUStringBuffer aBuffer; + if ( rInfo.DateValues ) + { + lcl_AppendDateStr( aBuffer, fStartValue, pFormatter ); + aBuffer.appendAscii( " - " ); // with spaces + lcl_AppendDateStr( aBuffer, fEndValue, pFormatter ); + } + else + { + rtl::math::doubleToUStringBuffer( aBuffer, fStartValue, rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, cDecSeparator, true ); + aBuffer.append( (sal_Unicode) '-' ); + rtl::math::doubleToUStringBuffer( aBuffer, fEndValue, rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, cDecSeparator, true ); + } + + return aBuffer.makeStringAndClear(); +} + +String lcl_GetSpecialNumGroupName( double fValue, bool bFirst, sal_Unicode cDecSeparator, + bool bDateValues, SvNumberFormatter* pFormatter ) +{ + DBG_ASSERT( cDecSeparator != 0, "cDecSeparator not initialized" ); + + rtl::OUStringBuffer aBuffer; + aBuffer.append((sal_Unicode)( bFirst ? '<' : '>' )); + if ( bDateValues ) + lcl_AppendDateStr( aBuffer, fValue, pFormatter ); + else + rtl::math::doubleToUStringBuffer( aBuffer, fValue, rtl_math_StringFormat_Automatic, + rtl_math_DecimalPlaces_Max, cDecSeparator, true ); + return aBuffer.makeStringAndClear(); +} + +inline bool IsInteger( double fValue ) +{ + return rtl::math::approxEqual( fValue, rtl::math::approxFloor(fValue) ); +} + +const TypedScStrCollection& ScDPNumGroupDimension::GetNumEntries( + const TypedScStrCollection& rOriginal, ScDocument* pDoc ) const +{ + if ( !pCollection ) + { + SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); + + pCollection = new TypedScStrCollection(); + if ( pDateHelper ) + pDateHelper->FillColumnEntries( *pCollection, rOriginal, pFormatter ); + else + { + // Copy textual entries. + // Also look through the source entries for non-integer numbers, minimum and maximum. + // GetNumEntries (GetColumnEntries) must be called before accessing the groups + // (this in ensured by calling ScDPLevel::GetMembersObject for all column/row/page + // dimensions before iterating over the values). + + cDecSeparator = ScGlobal::pLocaleData->getNumDecimalSep().GetChar(0); + + // non-integer GroupInfo values count, too + bHasNonInteger = ( !aGroupInfo.AutoStart && !IsInteger( aGroupInfo.Start ) ) || + ( !aGroupInfo.AutoEnd && !IsInteger( aGroupInfo.End ) ) || + !IsInteger( aGroupInfo.Step ); + double fSourceMin = 0.0; + double fSourceMax = 0.0; + bool bFirst = true; + + USHORT nOriginalCount = rOriginal.GetCount(); + for (USHORT nOriginalPos=0; nOriginalPos<nOriginalCount; nOriginalPos++) + { + const TypedStrData& rStrData = *rOriginal[nOriginalPos]; + if ( rStrData.IsStrData() ) + { + // string data: just copy + TypedStrData* pNew = new TypedStrData( rStrData ); + if ( !pCollection->Insert( pNew ) ) + delete pNew; + } + else + { + double fSourceValue = rStrData.GetValue(); + if ( bFirst ) + { + fSourceMin = fSourceMax = fSourceValue; + bFirst = false; + } + else + { + if ( fSourceValue < fSourceMin ) + fSourceMin = fSourceValue; + if ( fSourceValue > fSourceMax ) + fSourceMax = fSourceValue; + } + if ( !bHasNonInteger && !IsInteger( fSourceValue ) ) + { + // if any non-integer numbers are involved, the group labels are + // shown including their upper limit + bHasNonInteger = true; + } + } + } + + if ( aGroupInfo.DateValues ) + { + // special handling for dates: always integer, round down limits + bHasNonInteger = false; + fSourceMin = rtl::math::approxFloor( fSourceMin ); + fSourceMax = rtl::math::approxFloor( fSourceMax ) + 1; + } + + if ( aGroupInfo.AutoStart ) + const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.Start = fSourceMin; + if ( aGroupInfo.AutoEnd ) + const_cast<ScDPNumGroupDimension*>(this)->aGroupInfo.End = fSourceMax; + + //! limit number of entries? + + long nLoopCount = 0; + double fLoop = aGroupInfo.Start; + + // Use "less than" instead of "less or equal" for the loop - don't create a group + // that consists only of the end value. Instead, the end value is then included + // in the last group (last group is bigger than the others). + // The first group has to be created nonetheless. GetNumGroupForValue has corresponding logic. + + bool bFirstGroup = true; + while ( bFirstGroup || ( fLoop < aGroupInfo.End && !rtl::math::approxEqual( fLoop, aGroupInfo.End ) ) ) + { + String aName = lcl_GetNumGroupName( fLoop, aGroupInfo, bHasNonInteger, cDecSeparator, pFormatter ); + // create a numerical entry to ensure proper sorting + // (in FillMemberResults this needs special handling) + TypedStrData* pNew = new TypedStrData( aName, fLoop, SC_STRTYPE_VALUE ); + if ( !pCollection->Insert( pNew ) ) + delete pNew; + + ++nLoopCount; + fLoop = aGroupInfo.Start + nLoopCount * aGroupInfo.Step; + bFirstGroup = false; + + // ScDPItemData values are compared with approxEqual + } + + String aFirstName = lcl_GetSpecialNumGroupName( aGroupInfo.Start, true, cDecSeparator, aGroupInfo.DateValues, pFormatter ); + TypedStrData* pFirstEntry = new TypedStrData( aFirstName, aGroupInfo.Start - aGroupInfo.Step, SC_STRTYPE_VALUE ); + if ( !pCollection->Insert( pFirstEntry ) ) + delete pFirstEntry; + + String aLastName = lcl_GetSpecialNumGroupName( aGroupInfo.End, false, cDecSeparator, aGroupInfo.DateValues, pFormatter ); + TypedStrData* pLastEntry = new TypedStrData( aLastName, aGroupInfo.End + aGroupInfo.Step, SC_STRTYPE_VALUE ); + if ( !pCollection->Insert( pLastEntry ) ) + delete pLastEntry; + } + } + return *pCollection; +} + +// ----------------------------------------------------------------------- + +String lcl_GetNumGroupForValue( double fValue, const ScDPNumGroupInfo& rInfo, bool bHasNonInteger, + sal_Unicode cDecSeparator, double& rGroupValue, ScDocument* pDoc ) +{ + SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); + + if ( fValue < rInfo.Start && !rtl::math::approxEqual( fValue, rInfo.Start ) ) + { + rGroupValue = rInfo.Start - rInfo.Step; + return lcl_GetSpecialNumGroupName( rInfo.Start, true, cDecSeparator, rInfo.DateValues, pFormatter ); + } + + if ( fValue > rInfo.End && !rtl::math::approxEqual( fValue, rInfo.End ) ) + { + rGroupValue = rInfo.End + rInfo.Step; + return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter ); + } + + double fDiff = fValue - rInfo.Start; + double fDiv = rtl::math::approxFloor( fDiff / rInfo.Step ); + double fGroupStart = rInfo.Start + fDiv * rInfo.Step; + + if ( rtl::math::approxEqual( fGroupStart, rInfo.End ) && + !rtl::math::approxEqual( fGroupStart, rInfo.Start ) ) + { + if ( !rInfo.DateValues ) + { + // A group that would consist only of the end value is not created, + // instead the value is included in the last group before. So the + // previous group is used if the calculated group start value is the + // selected end value. + + fDiv -= 1.0; + fGroupStart = rInfo.Start + fDiv * rInfo.Step; + } + else + { + // For date values, the end value is instead treated as above the limit + // if it would be a group of its own. + + rGroupValue = rInfo.End + rInfo.Step; + return lcl_GetSpecialNumGroupName( rInfo.End, false, cDecSeparator, rInfo.DateValues, pFormatter ); + } + } + + rGroupValue = fGroupStart; + + return lcl_GetNumGroupName( fGroupStart, rInfo, bHasNonInteger, cDecSeparator, pFormatter ); +} + +ScDPGroupTableData::ScDPGroupTableData( ScDPTableData* pSource, ScDocument* pDocument ) : + ScDPTableData(pDocument), + pSourceData( pSource ), + pDoc( pDocument ) +{ + DBG_ASSERT( pSource, "ScDPGroupTableData: pSource can't be NULL" ); + + CreateCacheTable(); + nSourceCount = pSource->GetColumnCount(); // real columns, excluding data layout + pNumGroups = new ScDPNumGroupDimension[nSourceCount]; +} + +ScDPGroupTableData::~ScDPGroupTableData() +{ + delete[] pNumGroups; + delete pSourceData; +} + +void ScDPGroupTableData::AddGroupDimension( const ScDPGroupDimension& rGroup ) +{ + ScDPGroupDimension aNewGroup( rGroup ); + aNewGroup.SetGroupDim( GetColumnCount() ); // new dimension will be at the end + aGroups.push_back( aNewGroup ); + aGroupNames.insert( OUString(aNewGroup.GetName()) ); +} + +void ScDPGroupTableData::SetNumGroupDimension( long nIndex, const ScDPNumGroupDimension& rGroup ) +{ + if ( nIndex < nSourceCount ) + { + pNumGroups[nIndex] = rGroup; + + // automatic minimum / maximum is handled in GetNumEntries + } +} + +long ScDPGroupTableData::GetDimensionIndex( const String& rName ) +{ + for (long i=0; i<nSourceCount; i++) // nSourceCount excludes data layout + if ( pSourceData->getDimensionName(i) == rName ) //! ignore case? + return i; + + return -1; // none +} + +long ScDPGroupTableData::GetColumnCount() +{ + return nSourceCount + aGroups.size(); +} + +bool ScDPGroupTableData::IsNumGroupDimension( long nDimension ) const +{ + return ( nDimension < nSourceCount && pNumGroups[nDimension].GetInfo().Enable ); +} + +void ScDPGroupTableData::GetNumGroupInfo( long nDimension, ScDPNumGroupInfo& rInfo, + bool& rNonInteger, sal_Unicode& rDecimal ) +{ + if ( nDimension < nSourceCount ) + { + rInfo = pNumGroups[nDimension].GetInfo(); + rNonInteger = pNumGroups[nDimension].HasNonInteger(); + rDecimal = pNumGroups[nDimension].GetDecSeparator(); + } +} + +const TypedScStrCollection& ScDPGroupTableData::GetColumnEntries(long nColumn) +{ + // date handling is in ScDPGroupDimension::GetColumnEntries / ScDPNumGroupDimension::GetNumEntries + // (to use the pCollection members) + + if ( nColumn >= nSourceCount ) + { + if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension? + nColumn = nSourceCount; // index of data layout in source data + else + { + const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount]; + long nSourceDim = rGroupDim.GetSourceDim(); + // collection is cached at pSourceData, GetColumnEntries can be called every time + const TypedScStrCollection& rOriginal = pSourceData->GetColumnEntries( nSourceDim ); + return rGroupDim.GetColumnEntries( rOriginal, pDoc ); + } + } + + if ( IsNumGroupDimension( nColumn ) ) + { + // dimension number is unchanged for numerical groups + const TypedScStrCollection& rOriginal = pSourceData->GetColumnEntries( nColumn ); + return pNumGroups[nColumn].GetNumEntries( rOriginal, pDoc ); + } + + return pSourceData->GetColumnEntries( nColumn ); +} + +String ScDPGroupTableData::getDimensionName(long nColumn) +{ + if ( nColumn >= nSourceCount ) + { + if ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension? + nColumn = nSourceCount; // index of data layout in source data + else + return aGroups[nColumn - nSourceCount].GetName(); + } + + return pSourceData->getDimensionName( nColumn ); +} + +BOOL ScDPGroupTableData::getIsDataLayoutDimension(long nColumn) +{ + // position of data layout dimension is moved from source data + return ( nColumn == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ); // data layout dimension? +} + +BOOL ScDPGroupTableData::IsDateDimension(long nDim) +{ + if ( nDim >= nSourceCount ) + { + if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension? + nDim = nSourceCount; // index of data layout in source data + else + nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension + } + + return pSourceData->IsDateDimension( nDim ); +} + +UINT32 ScDPGroupTableData::GetNumberFormat(long nDim) +{ + if ( nDim >= nSourceCount ) + { + if ( nDim == sal::static_int_cast<long>( nSourceCount + aGroups.size() ) ) // data layout dimension? + nDim = nSourceCount; // index of data layout in source data + else + nDim = aGroups[nDim - nSourceCount].GetSourceDim(); // look at original dimension + } + + return pSourceData->GetNumberFormat( nDim ); +} + +void ScDPGroupTableData::DisposeData() +{ + for ( ScDPGroupDimensionVec::iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + aIter->DisposeData(); + + for ( long i=0; i<nSourceCount; i++ ) + pNumGroups[i].DisposeData(); + + pSourceData->DisposeData(); +} + +void ScDPGroupTableData::SetEmptyFlags( BOOL bIgnoreEmptyRows, BOOL bRepeatIfEmpty ) +{ + pSourceData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty ); +} + +bool ScDPGroupTableData::IsRepeatIfEmpty() +{ + return pSourceData->IsRepeatIfEmpty(); +} + +void ScDPGroupTableData::CreateCacheTable() +{ + pSourceData->CreateCacheTable(); +} + +void ScDPGroupTableData::ModifyFilterCriteria(vector<ScDPCacheTable::Criterion>& rCriteria) +{ + typedef hash_map<long, const ScDPGroupDimension*> GroupFieldMapType; + GroupFieldMapType aGroupFieldIds; + { + ScDPGroupDimensionVec::const_iterator itr = aGroups.begin(), itrEnd = aGroups.end(); + for (; itr != itrEnd; ++itr) + aGroupFieldIds.insert( hash_map<long, const ScDPGroupDimension*>::value_type(itr->GetGroupDim(), &(*itr)) ); + } + + vector<ScDPCacheTable::Criterion> aNewCriteria; + aNewCriteria.reserve(rCriteria.size() + aGroups.size()); + + // Go through all the filtered field names and process them appropriately. + + vector<ScDPCacheTable::Criterion>::const_iterator itrEnd = rCriteria.end(); + GroupFieldMapType::const_iterator itrGrpEnd = aGroupFieldIds.end(); + for (vector<ScDPCacheTable::Criterion>::const_iterator itr = rCriteria.begin(); itr != itrEnd; ++itr) + { + ScDPCacheTable::SingleFilter* pFilter = dynamic_cast<ScDPCacheTable::SingleFilter*>(itr->mpFilter.get()); + if (!pFilter) + // We expect this to be a single filter. + continue; + + GroupFieldMapType::const_iterator itrGrp = aGroupFieldIds.find(itr->mnFieldIndex); + if (itrGrp == itrGrpEnd) + { + if (IsNumGroupDimension(itr->mnFieldIndex)) + { + // internal number group field + const ScDPNumGroupDimension& rNumGrpDim = pNumGroups[itr->mnFieldIndex]; + const ScDPDateGroupHelper* pDateHelper = rNumGrpDim.GetDateHelper(); + if (!pDateHelper) + { + // What do we do here !? + continue; + } + + ScDPCacheTable::Criterion aCri; + aCri.mnFieldIndex = itr->mnFieldIndex; + aCri.mpFilter.reset(new ScDPGroupDateFilter( + pFilter->getMatchValue(), pDateHelper->GetDatePart(), + pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo())); + + aNewCriteria.push_back(aCri); + } + else + { + // This is a regular source field. + aNewCriteria.push_back(*itr); + } + } + else + { + // This is an ordinary group field or external number group field. + + const ScDPGroupDimension* pGrpDim = itrGrp->second; + long nSrcDim = pGrpDim->GetSourceDim(); + const ScDPDateGroupHelper* pDateHelper = pGrpDim->GetDateHelper(); + + if (pDateHelper) + { + // external number group + ScDPCacheTable::Criterion aCri; + aCri.mnFieldIndex = nSrcDim; // use the source dimension, not the group dimension. + aCri.mpFilter.reset(new ScDPGroupDateFilter( + pFilter->getMatchValue(), pDateHelper->GetDatePart(), + pDoc->GetFormatTable()->GetNullDate(), &pDateHelper->GetNumInfo())); + + aNewCriteria.push_back(aCri); + } + else + { + // normal group + + // Note that each group dimension may have multiple group names! + size_t nGroupItemCount = pGrpDim->GetItemCount(); + for (size_t i = 0; i < nGroupItemCount; ++i) + { + const ScDPGroupItem* pGrpItem = pGrpDim->GetGroupByIndex(i); + ScDPItemData aName; + aName.aString = pFilter->getMatchString(); + aName.fValue = pFilter->getMatchValue(); + aName.bHasValue = pFilter->hasValue(); + if (!pGrpItem || !pGrpItem->GetName().IsCaseInsEqual(aName)) + continue; + + ScDPCacheTable::Criterion aCri; + aCri.mnFieldIndex = nSrcDim; + aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter(GetSharedString())); + ScDPCacheTable::GroupFilter* pGrpFilter = + static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get()); + + pGrpItem->FillGroupFilter(*pGrpFilter); + aNewCriteria.push_back(aCri); + } + } + } + } + rCriteria.swap(aNewCriteria); +} + +void ScDPGroupTableData::FilterCacheTable(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims) +{ + vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria); + ModifyFilterCriteria(aNewCriteria); + pSourceData->FilterCacheTable(aNewCriteria, rCatDims); +} + +void ScDPGroupTableData::GetDrillDownData(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData) +{ + vector<ScDPCacheTable::Criterion> aNewCriteria(rCriteria); + ModifyFilterCriteria(aNewCriteria); + pSourceData->GetDrillDownData(aNewCriteria, rCatDims, rData); +} + +void ScDPGroupTableData::CalcResults(CalcInfo& rInfo, bool bAutoShow) +{ + // This CalcInfo instance is used only to retrive data from the original + // data source. + CalcInfo aInfoSrc = rInfo; + CopyFields(rInfo.aColLevelDims, aInfoSrc.aColLevelDims); + CopyFields(rInfo.aRowLevelDims, aInfoSrc.aRowLevelDims); + CopyFields(rInfo.aPageDims, aInfoSrc.aPageDims); + CopyFields(rInfo.aDataSrcCols, aInfoSrc.aDataSrcCols); + + const ScDPCacheTable& rCacheTable = pSourceData->GetCacheTable(); + sal_Int32 nRowSize = rCacheTable.getRowSize(); + for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow) + { + if (!rCacheTable.isRowActive(nRow)) + continue; + + CalcRowData aData; + FillRowDataFromCacheTable(nRow, rCacheTable, aInfoSrc, aData); + + if ( !rInfo.aColLevelDims.empty() ) + FillGroupValues(&aData.aColData[0], rInfo.aColLevelDims.size(), &rInfo.aColLevelDims[0]); + if ( !rInfo.aRowLevelDims.empty() ) + FillGroupValues(&aData.aRowData[0], rInfo.aRowLevelDims.size(), &rInfo.aRowLevelDims[0]); + if ( !rInfo.aPageDims.empty() ) + FillGroupValues(&aData.aPageData[0], rInfo.aPageDims.size(), &rInfo.aPageDims[0]); + + ProcessRowData(rInfo, aData, bAutoShow); + } +} + +const ScDPCacheTable& ScDPGroupTableData::GetCacheTable() const +{ + return pSourceData->GetCacheTable(); +} + +void ScDPGroupTableData::CopyFields(const vector<long>& rFieldDims, vector<long>& rNewFieldDims) +{ + size_t nCount = rFieldDims.size(); + if (!nCount) + return; + + long nGroupedColumns = aGroups.size(); + + rNewFieldDims.clear(); + rNewFieldDims.reserve(nCount); + for (size_t i = 0; i < nCount; ++i) + { + if ( rFieldDims[i] >= nSourceCount ) + { + if ( rFieldDims[i] == nSourceCount + nGroupedColumns ) + // data layout in source + rNewFieldDims.push_back(nSourceCount); + else + { + // original dimension + long n = rFieldDims[i] - nSourceCount; + rNewFieldDims.push_back(aGroups[n].GetSourceDim()); + } + } + else + rNewFieldDims.push_back(rFieldDims[i]); + } +} + +void ScDPGroupTableData::FillGroupValues( ScDPItemData* pItemData, long nCount, const long* pDims ) +{ + long nGroupedColumns = aGroups.size(); + + for (long nDim=0; nDim<nCount; nDim++) + { + const ScDPDateGroupHelper* pDateHelper = NULL; + + long nColumn = pDims[nDim]; + if ( nColumn >= nSourceCount && nColumn < nSourceCount + nGroupedColumns ) + { + const ScDPGroupDimension& rGroupDim = aGroups[nColumn - nSourceCount]; + pDateHelper = rGroupDim.GetDateHelper(); + if ( !pDateHelper ) // date is handled below + { + const ScDPGroupItem* pGroupItem = rGroupDim.GetGroupForData( pItemData[nDim] ); + if ( pGroupItem ) + pItemData[nDim] = pGroupItem->GetName(); + // if no group is found, keep the original name + } + } + else if ( IsNumGroupDimension( nColumn ) ) + { + pDateHelper = pNumGroups[nColumn].GetDateHelper(); + if ( !pDateHelper ) // date is handled below + { + if ( pItemData[nDim].bHasValue ) + { + ScDPNumGroupInfo aNumInfo; + bool bHasNonInteger = false; + sal_Unicode cDecSeparator = 0; + GetNumGroupInfo( nColumn, aNumInfo, bHasNonInteger, cDecSeparator ); + double fGroupValue; + String aGroupName = lcl_GetNumGroupForValue( pItemData[nDim].fValue, + aNumInfo, bHasNonInteger, cDecSeparator, fGroupValue, pDoc ); + + // consistent with TypedStrData in GetNumEntries + pItemData[nDim] = ScDPItemData( aGroupName, fGroupValue, TRUE ); + } + // else (textual) keep original value + } + } + + if ( pDateHelper ) + { + if ( pItemData[nDim].bHasValue ) + { + sal_Int32 nPartValue = lcl_GetDatePartValue( + pItemData[nDim].fValue, pDateHelper->GetDatePart(), pDoc->GetFormatTable(), + &pDateHelper->GetNumInfo() ); + pItemData[nDim] = ScDPItemData( String(), nPartValue, TRUE ); + } + } + } +} + +BOOL ScDPGroupTableData::IsBaseForGroup(long nDim) const +{ + for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + { + const ScDPGroupDimension& rDim = *aIter; + if ( rDim.GetSourceDim() == nDim ) + return TRUE; + } + + return FALSE; +} + +long ScDPGroupTableData::GetGroupBase(long nGroupDim) const +{ + for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + { + const ScDPGroupDimension& rDim = *aIter; + if ( rDim.GetGroupDim() == nGroupDim ) + return rDim.GetSourceDim(); + } + + return -1; // none +} + +BOOL ScDPGroupTableData::IsNumOrDateGroup(long nDimension) const +{ + // Virtual method from ScDPTableData, used in result data to force text labels. + + if ( nDimension < nSourceCount ) + { + return pNumGroups[nDimension].GetInfo().Enable || + pNumGroups[nDimension].GetDateHelper(); + } + + for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + { + const ScDPGroupDimension& rDim = *aIter; + if ( rDim.GetGroupDim() == nDimension ) + return ( rDim.GetDateHelper() != NULL ); + } + + return FALSE; +} + +BOOL ScDPGroupTableData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex, + const ScDPItemData& rBaseData, long nBaseIndex ) const +{ + for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + { + const ScDPGroupDimension& rDim = *aIter; + if ( rDim.GetGroupDim() == nGroupIndex && rDim.GetSourceDim() == nBaseIndex ) + { + const ScDPDateGroupHelper* pGroupDateHelper = rDim.GetDateHelper(); + if ( pGroupDateHelper ) + { + //! transform rBaseData (innermost date part) + //! -> always do "HasCommonElement" style comparison + //! (only Quarter, Month, Day affected) + + const ScDPDateGroupHelper* pBaseDateHelper = NULL; + if ( nBaseIndex < nSourceCount ) + pBaseDateHelper = pNumGroups[nBaseIndex].GetDateHelper(); + + // If there's a date group dimension, the base dimension must have + // date group information, too. + if ( !pBaseDateHelper ) + { + DBG_ERROR( "mix of date and non-date groups" ); + return TRUE; + } + + sal_Int32 nGroupPart = pGroupDateHelper->GetDatePart(); + sal_Int32 nBasePart = pBaseDateHelper->GetDatePart(); + return lcl_DateContained( nGroupPart, rGroupData, nBasePart, rBaseData ); + } + else + { + // If the item is in a group, only that group is valid. + // If the item is not in any group, its own name is valid. + + const ScDPGroupItem* pGroup = rDim.GetGroupForData( rBaseData ); + return pGroup ? pGroup->GetName().IsCaseInsEqual( rGroupData ) : + rGroupData.IsCaseInsEqual( rBaseData ); + } + } + } + + DBG_ERROR("IsInGroup: no group dimension found"); + return TRUE; +} + +BOOL ScDPGroupTableData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex, + const ScDPItemData& rSecondData, long nSecondIndex ) const +{ + const ScDPGroupDimension* pFirstDim = NULL; + const ScDPGroupDimension* pSecondDim = NULL; + for ( ScDPGroupDimensionVec::const_iterator aIter(aGroups.begin()); aIter != aGroups.end(); aIter++ ) + { + const ScDPGroupDimension* pDim = &(*aIter); + if ( pDim->GetGroupDim() == nFirstIndex ) + pFirstDim = pDim; + else if ( pDim->GetGroupDim() == nSecondIndex ) + pSecondDim = pDim; + } + if ( pFirstDim && pSecondDim ) + { + const ScDPDateGroupHelper* pFirstDateHelper = pFirstDim->GetDateHelper(); + const ScDPDateGroupHelper* pSecondDateHelper = pSecondDim->GetDateHelper(); + if ( pFirstDateHelper || pSecondDateHelper ) + { + // If one is a date group dimension, the other one must be, too. + if ( !pFirstDateHelper || !pSecondDateHelper ) + { + DBG_ERROR( "mix of date and non-date groups" ); + return TRUE; + } + + sal_Int32 nFirstPart = pFirstDateHelper->GetDatePart(); + sal_Int32 nSecondPart = pSecondDateHelper->GetDatePart(); + return lcl_DateContained( nFirstPart, rFirstData, nSecondPart, rSecondData ); + } + + const ScDPGroupItem* pFirstItem = pFirstDim->GetGroupForName( rFirstData ); + const ScDPGroupItem* pSecondItem = pSecondDim->GetGroupForName( rSecondData ); + if ( pFirstItem && pSecondItem ) + { + // two existing groups -> TRUE if they have a common element + return pFirstItem->HasCommonElement( *pSecondItem ); + } + else if ( pFirstItem ) + { + // "automatic" group contains only its own name + return pFirstItem->HasElement( rSecondData ); + } + else if ( pSecondItem ) + { + // "automatic" group contains only its own name + return pSecondItem->HasElement( rFirstData ); + } + else + { + // no groups -> TRUE if equal + return rFirstData.IsCaseInsEqual( rSecondData ); + } + } + + DBG_ERROR("HasCommonElement: no group dimension found"); + return TRUE; +} + +// ----------------------------------------------------------------------- + diff --git a/sc/source/core/data/dpobject.cxx b/sc/source/core/data/dpobject.cxx new file mode 100644 index 000000000000..787875fcb5ff --- /dev/null +++ b/sc/source/core/data/dpobject.cxx @@ -0,0 +1,2464 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpobject.cxx,v $ + * $Revision: 1.23.30.5 $ + * + * 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 "dpobject.hxx" +#include "dptabsrc.hxx" +#include "dpsave.hxx" +#include "dpdimsave.hxx" +#include "dpoutput.hxx" +#include "dpshttab.hxx" +#include "dpsdbtab.hxx" +#include "dpgroup.hxx" +#include "document.hxx" +#include "rechead.hxx" +#include "pivot.hxx" // PIVOT_DATA_FIELD +#include "dapiuno.hxx" // ScDataPilotConversion +#include "miscuno.hxx" +#include "scerrors.hxx" +#include "refupdat.hxx" +#include "scresid.hxx" +#include "sc.hrc" +#include "attrib.hxx" +#include "scitems.hxx" +#include "unonames.hxx" + +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sheet/GeneralFunction.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> +#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp> +#include <com/sun/star/sheet/DataPilotTablePositionData.hpp> +#include <com/sun/star/sheet/DataPilotTablePositionType.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/lang/XSingleServiceFactory.hpp> +#include <com/sun/star/lang/XInitialization.hpp> +#include <com/sun/star/container/XContentEnumerationAccess.hpp> +#include <com/sun/star/sheet/XDrillDownDataSupplier.hpp> + +#include <comphelper/processfactory.hxx> +#include <tools/debug.hxx> +#include <svtools/zforlist.hxx> // IsNumberFormat + +#include <vector> + +using namespace com::sun::star; +using ::std::vector; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Any; +using ::com::sun::star::sheet::DataPilotTableHeaderData; +using ::com::sun::star::sheet::DataPilotTablePositionData; +using ::com::sun::star::beans::XPropertySet; +using ::rtl::OUString; + +// ----------------------------------------------------------------------- + +#define MAX_LABELS 256 //!!! from fieldwnd.hxx, must be moved to global.hxx + +// ----------------------------------------------------------------------- + +#define SCDPSOURCE_SERVICE "com.sun.star.sheet.DataPilotSource" + +// ----------------------------------------------------------------------- + +// incompatible versions of data pilot files +#define SC_DP_VERSION_CURRENT 6 + +// type of source data +#define SC_DP_SOURCE_SHEET 0 +#define SC_DP_SOURCE_DATABASE 1 +#define SC_DP_SOURCE_SERVICE 2 + +// ----------------------------------------------------------------------- + +//! move to a header file +#define DP_PROP_COLUMNGRAND "ColumnGrand" +#define DP_PROP_FUNCTION "Function" +#define DP_PROP_IGNOREEMPTY "IgnoreEmptyRows" +#define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension" +//#define DP_PROP_ISVISIBLE "IsVisible" +#define DP_PROP_ORIENTATION "Orientation" +#define DP_PROP_ORIGINAL "Original" +#define DP_PROP_POSITION "Position" +#define DP_PROP_REPEATIFEMPTY "RepeatIfEmpty" +#define DP_PROP_ROWGRAND "RowGrand" +#define DP_PROP_SHOWDETAILS "ShowDetails" +#define DP_PROP_SHOWEMPTY "ShowEmpty" +#define DP_PROP_SUBTOTALS "SubTotals" +#define DP_PROP_USEDHIERARCHY "UsedHierarchy" + +// ----------------------------------------------------------------------- + +USHORT lcl_GetDataGetOrientation( const uno::Reference<sheet::XDimensionsSupplier>& xSource ) +{ + long nRet = sheet::DataPilotFieldOrientation_HIDDEN; + if ( xSource.is() ) + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + long nIntCount = xIntDims->getCount(); + BOOL bFound = FALSE; + for (long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++) + { + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nIntDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + if ( xDimProp.is() ) + { + bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + //! error checking -- is "IsDataLayoutDimension" property required?? + if (bFound) + nRet = ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + } + } + } + return static_cast< USHORT >( nRet ); +} + +// ----------------------------------------------------------------------- + +ScDPObject::ScDPObject( ScDocument* pD ) : + pDoc( pD ), + pSaveData( NULL ), + pSheetDesc( NULL ), + pImpDesc( NULL ), + pServDesc( NULL ), + pOutput( NULL ), + bSettingsChanged( FALSE ), + bAlive( FALSE ), + bAllowMove( FALSE ), + nHeaderRows( 0 ) +{ +} + +ScDPObject::ScDPObject(const ScDPObject& r) : + ScDataObject(), + pDoc( r.pDoc ), + pSaveData( NULL ), + aTableName( r.aTableName ), + aTableTag( r.aTableTag ), + aOutRange( r.aOutRange ), + pSheetDesc( NULL ), + pImpDesc( NULL ), + pServDesc( NULL ), + pOutput( NULL ), + bSettingsChanged( FALSE ), + bAlive( FALSE ), + bAllowMove( FALSE ), + nHeaderRows( r.nHeaderRows ) +{ + if (r.pSaveData) + pSaveData = new ScDPSaveData(*r.pSaveData); + if (r.pSheetDesc) + pSheetDesc = new ScSheetSourceDesc(*r.pSheetDesc); + if (r.pImpDesc) + pImpDesc = new ScImportSourceDesc(*r.pImpDesc); + if (r.pServDesc) + pServDesc = new ScDPServiceDesc(*r.pServDesc); + // xSource (and pOutput) is not copied +} + +ScDPObject::~ScDPObject() +{ + delete pOutput; + delete pSaveData; + delete pSheetDesc; + delete pImpDesc; + delete pServDesc; +} + +ScDataObject* ScDPObject::Clone() const +{ + return new ScDPObject(*this); +} + +void ScDPObject::SetAlive(BOOL bSet) +{ + bAlive = bSet; +} + +void ScDPObject::SetAllowMove(BOOL bSet) +{ + bAllowMove = bSet; +} + +void ScDPObject::SetSaveData(const ScDPSaveData& rData) +{ + if ( pSaveData != &rData ) // API implementation modifies the original SaveData object + { + delete pSaveData; + pSaveData = new ScDPSaveData( rData ); + } + + InvalidateData(); // re-init source from SaveData +} + +void ScDPObject::SetOutRange(const ScRange& rRange) +{ + aOutRange = rRange; + + if ( pOutput ) + pOutput->SetPosition( rRange.aStart ); +} + +void ScDPObject::SetSheetDesc(const ScSheetSourceDesc& rDesc) +{ + if ( pSheetDesc && rDesc == *pSheetDesc ) + return; // nothing to do + + DELETEZ( pImpDesc ); + DELETEZ( pServDesc ); + + delete pImpDesc; + pSheetDesc = new ScSheetSourceDesc(rDesc); + + // make valid QueryParam + + pSheetDesc->aQueryParam.nCol1 = pSheetDesc->aSourceRange.aStart.Col(); + pSheetDesc->aQueryParam.nRow1 = pSheetDesc->aSourceRange.aStart.Row(); + pSheetDesc->aQueryParam.nCol2 = pSheetDesc->aSourceRange.aEnd.Col(); + pSheetDesc->aQueryParam.nRow2 = pSheetDesc->aSourceRange.aEnd.Row();; + pSheetDesc->aQueryParam.bHasHeader = TRUE; + + InvalidateSource(); // new source must be created +} + +void ScDPObject::SetImportDesc(const ScImportSourceDesc& rDesc) +{ + if ( pImpDesc && rDesc == *pImpDesc ) + return; // nothing to do + + DELETEZ( pSheetDesc ); + DELETEZ( pServDesc ); + + delete pImpDesc; + pImpDesc = new ScImportSourceDesc(rDesc); + + InvalidateSource(); // new source must be created +} + +void ScDPObject::SetServiceData(const ScDPServiceDesc& rDesc) +{ + if ( pServDesc && rDesc == *pServDesc ) + return; // nothing to do + + DELETEZ( pSheetDesc ); + DELETEZ( pImpDesc ); + + delete pServDesc; + pServDesc = new ScDPServiceDesc(rDesc); + + InvalidateSource(); // new source must be created +} + +void ScDPObject::WriteSourceDataTo( ScDPObject& rDest ) const +{ + if ( pSheetDesc ) + rDest.SetSheetDesc( *pSheetDesc ); + else if ( pImpDesc ) + rDest.SetImportDesc( *pImpDesc ); + else if ( pServDesc ) + rDest.SetServiceData( *pServDesc ); + + // name/tag are not source data, but needed along with source data + + rDest.aTableName = aTableName; + rDest.aTableTag = aTableTag; +} + +void ScDPObject::WriteTempDataTo( ScDPObject& rDest ) const +{ + rDest.nHeaderRows = nHeaderRows; +} + +BOOL ScDPObject::IsSheetData() const +{ + return ( pSheetDesc != NULL ); +} + +void ScDPObject::SetName(const String& rNew) +{ + aTableName = rNew; +} + +void ScDPObject::SetTag(const String& rNew) +{ + aTableTag = rNew; +} + +uno::Reference<sheet::XDimensionsSupplier> ScDPObject::GetSource() +{ + CreateObjects(); + return xSource; +} + +void ScDPObject::CreateOutput() +{ + CreateObjects(); + if (!pOutput) + { + BOOL bFilterButton = IsSheetData() && pSaveData && pSaveData->GetFilterButton(); + pOutput = new ScDPOutput( pDoc, xSource, aOutRange.aStart, bFilterButton ); + + long nOldRows = nHeaderRows; + nHeaderRows = pOutput->GetHeaderRows(); + + if ( bAllowMove && nHeaderRows != nOldRows ) + { + long nDiff = nOldRows - nHeaderRows; + if ( nOldRows == 0 ) + --nDiff; + if ( nHeaderRows == 0 ) + ++nDiff; + + long nNewRow = aOutRange.aStart.Row() + nDiff; + if ( nNewRow < 0 ) + nNewRow = 0; + + ScAddress aStart( aOutRange.aStart ); + aStart.SetRow( (USHORT) nNewRow ); + pOutput->SetPosition( aStart ); + + //! modify aOutRange? + + bAllowMove = FALSE; // use only once + } + } +} + +void ScDPObject::CreateObjects() +{ + // if groups are involved, create a new source with the ScDPGroupTableData + if ( bSettingsChanged && pSaveData && pSaveData->GetExistingDimensionData() ) + xSource = NULL; + + if (!xSource.is()) + { + //! cache DPSource and/or Output? + + DBG_ASSERT( bAlive, "CreateObjects on non-inserted DPObject" ); + + DELETEZ( pOutput ); // not valid when xSource is changed + + if ( pServDesc ) + { + xSource = CreateSource( *pServDesc ); + } + + if ( !xSource.is() ) // database or sheet data, or error in CreateSource + { + DBG_ASSERT( !pServDesc, "DPSource could not be created" ); + + ScDPTableData* pData = NULL; + if ( pImpDesc ) + { + // database data + pData = new ScDatabaseDPData( pDoc, *pImpDesc ); + } + else + { + // cell data + if (!pSheetDesc) + { + DBG_ERROR("no source descriptor"); + pSheetDesc = new ScSheetSourceDesc; // dummy defaults + } + pData = new ScSheetDPData( pDoc, *pSheetDesc ); + } + + // grouping (for cell or database data) + if ( pSaveData && pSaveData->GetExistingDimensionData() ) + { + ScDPGroupTableData* pGroupData = new ScDPGroupTableData( pData, pDoc ); + pSaveData->GetExistingDimensionData()->WriteToData( *pGroupData ); + pData = pGroupData; + } + + xSource = new ScDPSource( pData ); + } + + if (pSaveData) + pSaveData->WriteToSource( xSource ); + } + else if (bSettingsChanged) + { + DELETEZ( pOutput ); // not valid when xSource is changed + + uno::Reference<util::XRefreshable> xRef( xSource, uno::UNO_QUERY ); + if (xRef.is()) + { + try + { + xRef->refresh(); + } + catch(uno::Exception&) + { + DBG_ERROR("exception in refresh"); + } + } + + if (pSaveData) + pSaveData->WriteToSource( xSource ); + } + bSettingsChanged = FALSE; +} + +void ScDPObject::InvalidateData() +{ + bSettingsChanged = TRUE; +} + +void ScDPObject::InvalidateSource() +{ + xSource = NULL; +} + +ScRange ScDPObject::GetNewOutputRange( BOOL& rOverflow ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + rOverflow = pOutput->HasError(); // range overflow or exception from source + if ( rOverflow ) + return ScRange( aOutRange.aStart ); + else + { + // don't store the result in aOutRange, because nothing has been output yet + return pOutput->GetOutputRange(); + } +} + +void ScDPObject::Output( const ScAddress& rPos ) +{ + // clear old output area + pDoc->DeleteAreaTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(), + aOutRange.aEnd.Col(), aOutRange.aEnd.Row(), + aOutRange.aStart.Tab(), IDF_ALL ); + pDoc->RemoveFlagsTab( aOutRange.aStart.Col(), aOutRange.aStart.Row(), + aOutRange.aEnd.Col(), aOutRange.aEnd.Row(), + aOutRange.aStart.Tab(), SC_MF_AUTO ); + + CreateOutput(); // create xSource and pOutput if not already done + + pOutput->SetPosition( rPos ); + + pOutput->Output(); + + // aOutRange is always the range that was last output to the document + aOutRange = pOutput->GetOutputRange(); +} + +const ScRange ScDPObject::GetOutputRangeByType( sal_Int32 nType ) +{ + CreateOutput(); + + if (pOutput->HasError()) + return ScRange(aOutRange.aStart); + + return pOutput->GetOutputRange(nType); +} + +BOOL lcl_HasButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + return ((const ScMergeFlagAttr*)pDoc->GetAttr( nCol, nRow, nTab, ATTR_MERGE_FLAG ))->HasButton(); +} + +void ScDPObject::RefreshAfterLoad() +{ + // apply drop-down attribute, initialize nHeaderRows, without accessing the source + // (button attribute must be present) + + // simple test: block of button cells at the top, followed by an empty cell + + SCCOL nFirstCol = aOutRange.aStart.Col(); + SCROW nFirstRow = aOutRange.aStart.Row(); + SCTAB nTab = aOutRange.aStart.Tab(); + + SCROW nInitial = 0; + SCROW nOutRows = aOutRange.aEnd.Row() + 1 - aOutRange.aStart.Row(); + while ( nInitial + 1 < nOutRows && lcl_HasButton( pDoc, nFirstCol, nFirstRow + nInitial, nTab ) ) + ++nInitial; + + if ( nInitial + 1 < nOutRows && + pDoc->IsBlockEmpty( nTab, nFirstCol, nFirstRow + nInitial, nFirstCol, nFirstRow + nInitial ) && + aOutRange.aEnd.Col() > nFirstCol ) + { + BOOL bFilterButton = IsSheetData(); // when available, filter button setting must be checked here + + SCROW nSkip = bFilterButton ? 1 : 0; + for (SCROW nPos=nSkip; nPos<nInitial; nPos++) + pDoc->ApplyAttr( nFirstCol + 1, nFirstRow + nPos, nTab, ScMergeFlagAttr(SC_MF_AUTO) ); + + nHeaderRows = nInitial; + } + else + nHeaderRows = 0; // nothing found, no drop-down lists +} + +void ScDPObject::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + // Output area + + SCCOL nCol1 = aOutRange.aStart.Col(); + SCROW nRow1 = aOutRange.aStart.Row(); + SCTAB nTab1 = aOutRange.aStart.Tab(); + SCCOL nCol2 = aOutRange.aEnd.Col(); + SCROW nRow2 = aOutRange.aEnd.Row(); + SCTAB nTab2 = aOutRange.aEnd.Tab(); + + ScRefUpdateRes eRes = + ScRefUpdate::Update( pDoc, eUpdateRefMode, + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( eRes != UR_NOTHING ) + SetOutRange( ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ) ); + + // sheet source data + + if ( pSheetDesc ) + { + nCol1 = pSheetDesc->aSourceRange.aStart.Col(); + nRow1 = pSheetDesc->aSourceRange.aStart.Row(); + nTab1 = pSheetDesc->aSourceRange.aStart.Tab(); + nCol2 = pSheetDesc->aSourceRange.aEnd.Col(); + nRow2 = pSheetDesc->aSourceRange.aEnd.Row(); + nTab2 = pSheetDesc->aSourceRange.aEnd.Tab(); + + eRes = ScRefUpdate::Update( pDoc, eUpdateRefMode, + rRange.aStart.Col(), rRange.aStart.Row(), rRange.aStart.Tab(), + rRange.aEnd.Col(), rRange.aEnd.Row(), rRange.aEnd.Tab(), nDx, nDy, nDz, + nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + if ( eRes != UR_NOTHING ) + { + ScSheetSourceDesc aNewDesc; + aNewDesc.aSourceRange = ScRange( nCol1, nRow1, nTab1, nCol2, nRow2, nTab2 ); + + SCsCOL nDiffX = nCol1 - (SCsCOL) pSheetDesc->aSourceRange.aStart.Col(); + SCsROW nDiffY = nRow1 - (SCsROW) pSheetDesc->aSourceRange.aStart.Row(); + + aNewDesc.aQueryParam = pSheetDesc->aQueryParam; + aNewDesc.aQueryParam.nCol1 = sal::static_int_cast<SCCOL>( aNewDesc.aQueryParam.nCol1 + nDiffX ); + aNewDesc.aQueryParam.nCol2 = sal::static_int_cast<SCCOL>( aNewDesc.aQueryParam.nCol2 + nDiffX ); + aNewDesc.aQueryParam.nRow1 += nDiffY; //! used? + aNewDesc.aQueryParam.nRow2 += nDiffY; //! used? + SCSIZE nEC = aNewDesc.aQueryParam.GetEntryCount(); + for (SCSIZE i=0; i<nEC; i++) + if (aNewDesc.aQueryParam.GetEntry(i).bDoQuery) + aNewDesc.aQueryParam.GetEntry(i).nField += nDiffX; + + SetSheetDesc( aNewDesc ); // allocates new pSheetDesc + } + } +} + +BOOL ScDPObject::RefsEqual( const ScDPObject& r ) const +{ + if ( aOutRange != r.aOutRange ) + return FALSE; + + if ( pSheetDesc && r.pSheetDesc ) + { + if ( pSheetDesc->aSourceRange != r.pSheetDesc->aSourceRange ) + return FALSE; + } + else if ( pSheetDesc || r.pSheetDesc ) + { + DBG_ERROR("RefsEqual: SheetDesc set at only one object"); + return FALSE; + } + + return TRUE; +} + +void ScDPObject::WriteRefsTo( ScDPObject& r ) const +{ + r.SetOutRange( aOutRange ); + if ( pSheetDesc ) + r.SetSheetDesc( *pSheetDesc ); +} + +void ScDPObject::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData) +{ + CreateOutput(); + pOutput->GetPositionData(rPos, rPosData); +} + +bool ScDPObject::GetDataFieldPositionData( + const ScAddress& rPos, Sequence<sheet::DataPilotFieldFilter>& rFilters) +{ + CreateOutput(); + + vector<sheet::DataPilotFieldFilter> aFilters; + if (!pOutput->GetDataResultPositionData(aFilters, rPos)) + return false; + + sal_Int32 n = static_cast<sal_Int32>(aFilters.size()); + rFilters.realloc(n); + for (sal_Int32 i = 0; i < n; ++i) + rFilters[i] = aFilters[i]; + + return true; +} + +void ScDPObject::GetDrillDownData(const ScAddress& rPos, Sequence< Sequence<Any> >& rTableData) +{ + CreateOutput(); + + Reference<sheet::XDrillDownDataSupplier> xDrillDownData(xSource, UNO_QUERY); + if (!xDrillDownData.is()) + return; + + Sequence<sheet::DataPilotFieldFilter> filters; + if (!GetDataFieldPositionData(rPos, filters)) + return; + + rTableData = xDrillDownData->getDrillDownData(filters); +} + +BOOL ScDPObject::IsDimNameInUse( const String& rName ) const +{ + if ( xSource.is() ) + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + if ( xDimsName.is() ) + { + rtl::OUString aCompare( rName ); + uno::Sequence<rtl::OUString> aNames = xDimsName->getElementNames(); + long nCount = aNames.getLength(); + const rtl::OUString* pArr = aNames.getConstArray(); + for (long nPos=0; nPos<nCount; nPos++) + if ( pArr[nPos] == aCompare ) //! ignore case + return TRUE; + } + } + return FALSE; // not found +} + +String ScDPObject::GetDimName( long nDim, BOOL& rIsDataLayout ) +{ + rIsDataLayout = FALSE; + String aRet; + + if ( xSource.is() ) + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName ); + long nDimCount = xDims->getCount(); + if ( nDim < nDimCount ) + { + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + if ( xDimName.is() && xDimProp.is() ) + { + BOOL bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + //! error checking -- is "IsDataLayoutDimension" property required?? + + rtl::OUString aName; + try + { + aName = xDimName->getName(); + } + catch(uno::Exception&) + { + } + if ( bData ) + rIsDataLayout = TRUE; + else + aRet = String( aName ); + } + } + } + + return aRet; +} + +BOOL ScDPObject::IsDuplicated( long nDim ) +{ + BOOL bDuplicated = FALSE; + if ( xSource.is() ) + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName ); + long nDimCount = xDims->getCount(); + if ( nDim < nDimCount ) + { + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + if ( xDimProp.is() ) + { + try + { + uno::Any aOrigAny = xDimProp->getPropertyValue( + rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) ); + uno::Reference<uno::XInterface> xIntOrig; + if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() ) + bDuplicated = TRUE; + } + catch(uno::Exception&) + { + } + } + } + } + return bDuplicated; +} + +long ScDPObject::GetDimCount() +{ + long nRet = 0; + if ( xSource.is() ) + { + try + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + if ( xDimsName.is() ) + nRet = xDimsName->getElementNames().getLength(); + } + catch(uno::Exception&) + { + } + } + return nRet; +} + +void ScDPObject::FillPageList( TypedScStrCollection& rStrings, long nField ) +{ + //! merge members access with ToggleDetails? + + //! convert field index to dimension index? + + DBG_ASSERT( xSource.is(), "no source" ); + if ( !xSource.is() ) return; + + uno::Reference<container::XNamed> xDim; + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + long nIntCount = xIntDims->getCount(); + if ( nField < nIntCount ) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( + xIntDims->getByIndex(nField) ); + xDim = uno::Reference<container::XNamed>( xIntDim, uno::UNO_QUERY ); + } + DBG_ASSERT( xDim.is(), "dimension not found" ); + if ( !xDim.is() ) return; + + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) ); + long nLevel = 0; + + long nHierCount = 0; + uno::Reference<container::XIndexAccess> xHiers; + uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies(); + xHiers = new ScNameToIndexAccess( xHiersName ); + nHierCount = xHiers->getCount(); + } + uno::Reference<uno::XInterface> xHier; + if ( nHierarchy < nHierCount ) + xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex(nHierarchy) ); + DBG_ASSERT( xHier.is(), "hierarchy not found" ); + if ( !xHier.is() ) return; + + long nLevCount = 0; + uno::Reference<container::XIndexAccess> xLevels; + uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY ); + if ( xLevSupp.is() ) + { + uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels(); + xLevels = new ScNameToIndexAccess( xLevsName ); + nLevCount = xLevels->getCount(); + } + uno::Reference<uno::XInterface> xLevel; + if ( nLevel < nLevCount ) + xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex(nLevel) ); + DBG_ASSERT( xLevel.is(), "level not found" ); + if ( !xLevel.is() ) return; + + uno::Reference<container::XNameAccess> xMembers; + uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY ); + if ( xMbrSupp.is() ) + xMembers = xMbrSupp->getMembers(); + DBG_ASSERT( xMembers.is(), "members not found" ); + if ( !xMembers.is() ) return; + + uno::Sequence<rtl::OUString> aNames = xMembers->getElementNames(); + long nNameCount = aNames.getLength(); + const rtl::OUString* pNameArr = aNames.getConstArray(); + for (long nPos = 0; nPos < nNameCount; ++nPos) + { + // Make sure to insert only visible members. + Reference<XPropertySet> xPropSet(xMembers->getByName(pNameArr[nPos]), UNO_QUERY); + sal_Bool bVisible = false; + if (xPropSet.is()) + { + Any any = xPropSet->getPropertyValue(OUString::createFromAscii(SC_UNO_ISVISIBL)); + any >>= bVisible; + } + + if (bVisible) + { + // use the order from getElementNames + TypedStrData* pData = new TypedStrData( pNameArr[nPos] ); + if ( !rStrings.AtInsert( rStrings.GetCount(), pData ) ) + delete pData; + } + } + + // add "-all-" entry to the top (unsorted) + TypedStrData* pAllData = new TypedStrData( String( ScResId( SCSTR_ALL ) ) ); //! separate string? (also output) + if ( !rStrings.AtInsert( 0, pAllData ) ) + delete pAllData; +} + +void ScDPObject::GetHeaderPositionData(const ScAddress& rPos, DataPilotTableHeaderData& rData) +{ + using namespace ::com::sun::star::sheet::DataPilotTablePositionType; + + CreateOutput(); // create xSource and pOutput if not already done + + // Reset member values to invalid state. + rData.Dimension = rData.Hierarchy = rData.Level = -1; + rData.Flags = 0; + + DataPilotTablePositionData aPosData; + pOutput->GetPositionData(rPos, aPosData); + const sal_Int32 nPosType = aPosData.PositionType; + if (nPosType == COLUMN_HEADER || nPosType == ROW_HEADER) + aPosData.PositionData >>= rData; +} + +// Returns TRUE on success and stores the result in rTarget +BOOL ScDPObject::GetPivotData( ScDPGetPivotDataField& rTarget, + const std::vector< ScDPGetPivotDataField >& rFilters ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + return pOutput->GetPivotData( rTarget, rFilters ); +} + +BOOL ScDPObject::IsFilterButton( const ScAddress& rPos ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + return pOutput->IsFilterButton( rPos ); +} + +long ScDPObject::GetHeaderDim( const ScAddress& rPos, USHORT& rOrient ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + return pOutput->GetHeaderDim( rPos, rOrient ); +} + +BOOL ScDPObject::GetHeaderDrag( const ScAddress& rPos, BOOL bMouseLeft, BOOL bMouseTop, long nDragDim, + Rectangle& rPosRect, USHORT& rOrient, long& rDimPos ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + return pOutput->GetHeaderDrag( rPos, bMouseLeft, bMouseTop, nDragDim, rPosRect, rOrient, rDimPos ); +} + +void ScDPObject::GetMemberResultNames( ScStrCollection& rNames, long nDimension ) +{ + CreateOutput(); // create xSource and pOutput if not already done + + pOutput->GetMemberResultNames( rNames, nDimension ); // used only with table data -> level not needed +} + +bool lcl_Dequote( const String& rSource, xub_StrLen nStartPos, xub_StrLen& rEndPos, String& rResult ) +{ + // nStartPos has to point to opening quote + + bool bRet = false; + const sal_Unicode cQuote = '\''; + + if ( rSource.GetChar(nStartPos) == cQuote ) + { + rtl::OUStringBuffer aBuffer; + xub_StrLen nPos = nStartPos + 1; + const xub_StrLen nLen = rSource.Len(); + + while ( nPos < nLen ) + { + const sal_Unicode cNext = rSource.GetChar(nPos); + if ( cNext == cQuote ) + { + if ( nPos+1 < nLen && rSource.GetChar(nPos+1) == cQuote ) + { + // double quote is used for an embedded quote + aBuffer.append( cNext ); // append one quote + ++nPos; // skip the next one + } + else + { + // end of quoted string + rResult = aBuffer.makeStringAndClear(); + rEndPos = nPos + 1; // behind closing quote + return true; + } + } + else + aBuffer.append( cNext ); + + ++nPos; + } + // no closing quote before the end of the string -> error (bRet still false) + } + + return bRet; +} + +struct ScGetPivotDataFunctionEntry +{ + const sal_Char* pName; + sheet::GeneralFunction eFunc; +}; + +bool lcl_ParseFunction( const String& rList, xub_StrLen nStartPos, xub_StrLen& rEndPos, sheet::GeneralFunction& rFunc ) +{ + static const ScGetPivotDataFunctionEntry aFunctions[] = + { + // our names + { "Sum", sheet::GeneralFunction_SUM }, + { "Count", sheet::GeneralFunction_COUNT }, + { "Average", sheet::GeneralFunction_AVERAGE }, + { "Max", sheet::GeneralFunction_MAX }, + { "Min", sheet::GeneralFunction_MIN }, + { "Product", sheet::GeneralFunction_PRODUCT }, + { "CountNums", sheet::GeneralFunction_COUNTNUMS }, + { "StDev", sheet::GeneralFunction_STDEV }, + { "StDevp", sheet::GeneralFunction_STDEVP }, + { "Var", sheet::GeneralFunction_VAR }, + { "VarP", sheet::GeneralFunction_VARP }, + // compatibility names + { "Count Nums", sheet::GeneralFunction_COUNTNUMS }, + { "StdDev", sheet::GeneralFunction_STDEV }, + { "StdDevp", sheet::GeneralFunction_STDEVP } + }; + + const xub_StrLen nListLen = rList.Len(); + while ( nStartPos < nListLen && rList.GetChar(nStartPos) == ' ' ) + ++nStartPos; + + bool bParsed = false; + bool bFound = false; + String aFuncStr; + xub_StrLen nFuncEnd = 0; + if ( nStartPos < nListLen && rList.GetChar(nStartPos) == '\'' ) + bParsed = lcl_Dequote( rList, nStartPos, nFuncEnd, aFuncStr ); + else + { + nFuncEnd = rList.Search( static_cast<sal_Unicode>(']'), nStartPos ); + if ( nFuncEnd != STRING_NOTFOUND ) + { + aFuncStr = rList.Copy( nStartPos, nFuncEnd - nStartPos ); + bParsed = true; + } + } + + if ( bParsed ) + { + aFuncStr.EraseLeadingAndTrailingChars( ' ' ); + + const sal_Int32 nFuncCount = sizeof(aFunctions) / sizeof(aFunctions[0]); + for ( sal_Int32 nFunc=0; nFunc<nFuncCount && !bFound; nFunc++ ) + { + if ( aFuncStr.EqualsIgnoreCaseAscii( aFunctions[nFunc].pName ) ) + { + rFunc = aFunctions[nFunc].eFunc; + bFound = true; + + while ( nFuncEnd < nListLen && rList.GetChar(nFuncEnd) == ' ' ) + ++nFuncEnd; + rEndPos = nFuncEnd; + } + } + } + + return bFound; +} + +bool lcl_IsAtStart( const String& rList, const String& rSearch, sal_Int32& rMatched, + bool bAllowBracket, sheet::GeneralFunction* pFunc ) +{ + sal_Int32 nMatchList = 0; + sal_Int32 nMatchSearch = 0; + sal_Unicode cFirst = rList.GetChar(0); + if ( cFirst == '\'' || cFirst == '[' ) + { + // quoted string or string in brackets must match completely + + String aDequoted; + xub_StrLen nQuoteEnd = 0; + bool bParsed = false; + + if ( cFirst == '\'' ) + bParsed = lcl_Dequote( rList, 0, nQuoteEnd, aDequoted ); + else if ( cFirst == '[' ) + { + // skip spaces after the opening bracket + + xub_StrLen nStartPos = 1; + const xub_StrLen nListLen = rList.Len(); + while ( nStartPos < nListLen && rList.GetChar(nStartPos) == ' ' ) + ++nStartPos; + + if ( rList.GetChar(nStartPos) == '\'' ) // quoted within the brackets? + { + if ( lcl_Dequote( rList, nStartPos, nQuoteEnd, aDequoted ) ) + { + // after the quoted string, there must be the closing bracket, optionally preceded by spaces, + // and/or a function name + while ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ' ' ) + ++nQuoteEnd; + + // semicolon separates function name + if ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ';' && pFunc ) + { + xub_StrLen nFuncEnd = 0; + if ( lcl_ParseFunction( rList, nQuoteEnd + 1, nFuncEnd, *pFunc ) ) + nQuoteEnd = nFuncEnd; + } + if ( nQuoteEnd < nListLen && rList.GetChar(nQuoteEnd) == ']' ) + { + ++nQuoteEnd; // include the closing bracket for the matched length + bParsed = true; + } + } + } + else + { + // implicit quoting to the closing bracket + + xub_StrLen nClosePos = rList.Search( static_cast<sal_Unicode>(']'), nStartPos ); + if ( nClosePos != STRING_NOTFOUND ) + { + xub_StrLen nNameEnd = nClosePos; + xub_StrLen nSemiPos = rList.Search( static_cast<sal_Unicode>(';'), nStartPos ); + if ( nSemiPos != STRING_NOTFOUND && nSemiPos < nClosePos && pFunc ) + { + xub_StrLen nFuncEnd = 0; + if ( lcl_ParseFunction( rList, nSemiPos + 1, nFuncEnd, *pFunc ) ) + nNameEnd = nSemiPos; + } + + aDequoted = rList.Copy( nStartPos, nNameEnd - nStartPos ); + aDequoted.EraseTrailingChars( ' ' ); // spaces before the closing bracket or semicolon + nQuoteEnd = nClosePos + 1; + bParsed = true; + } + } + } + + if ( bParsed && ScGlobal::GetpTransliteration()->isEqual( aDequoted, rSearch ) ) + { + nMatchList = nQuoteEnd; // match count in the list string, including quotes + nMatchSearch = rSearch.Len(); + } + } + else + { + // otherwise look for search string at the start of rList + ScGlobal::GetpTransliteration()->equals( rList, 0, rList.Len(), nMatchList, + rSearch, 0, rSearch.Len(), nMatchSearch ); + } + + if ( nMatchSearch == rSearch.Len() ) + { + // search string is at start of rList - look for following space or end of string + + bool bValid = false; + if ( sal::static_int_cast<xub_StrLen>(nMatchList) >= rList.Len() ) + bValid = true; + else + { + sal_Unicode cNext = rList.GetChar(sal::static_int_cast<xub_StrLen>(nMatchList)); + if ( cNext == ' ' || ( bAllowBracket && cNext == '[' ) ) + bValid = true; + } + + if ( bValid ) + { + rMatched = nMatchList; + return true; + } + } + + return false; +} + +BOOL ScDPObject::ParseFilters( ScDPGetPivotDataField& rTarget, + std::vector< ScDPGetPivotDataField >& rFilters, + const String& rFilterList ) +{ + // parse the string rFilterList into parameters for GetPivotData + + CreateObjects(); // create xSource if not already done + + std::vector<String> aDataNames; // data fields (source name) + std::vector<String> aGivenNames; // data fields (compound name) + std::vector<String> aFieldNames; // column/row/data fields + std::vector< uno::Sequence<rtl::OUString> > aFieldValues; + + // + // get all the field and item names + // + + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + sal_Int32 nDimCount = xIntDims->getCount(); + for ( sal_Int32 nDim = 0; nDim<nDimCount; nDim++ ) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nDim) ); + uno::Reference<container::XNamed> xDim( xIntDim, uno::UNO_QUERY ); + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY ); + BOOL bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + sal_Int32 nOrient = ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( !bDataLayout ) + { + if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) + { + String aSourceName; + String aGivenName; + ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xIntDim ); + aDataNames.push_back( aSourceName ); + aGivenNames.push_back( aGivenName ); + } + else if ( nOrient != sheet::DataPilotFieldOrientation_HIDDEN ) + { + // get level names, as in ScDPOutput + + uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() ); + sal_Int32 nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) ); + if ( nHierarchy >= xHiers->getCount() ) + nHierarchy = 0; + + uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface( + xHiers->getByIndex(nHierarchy) ); + uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() ); + sal_Int32 nLevCount = xLevels->getCount(); + for (sal_Int32 nLev=0; nLev<nLevCount; nLev++) + { + uno::Reference<uno::XInterface> xLevel = ScUnoHelpFunctions::AnyToInterface( + xLevels->getByIndex(nLev) ); + uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY ); + uno::Reference<sheet::XMembersSupplier> xLevSupp( xLevel, uno::UNO_QUERY ); + if ( xLevNam.is() && xLevSupp.is() ) + { + uno::Reference<container::XNameAccess> xMembers = xLevSupp->getMembers(); + + String aFieldName( xLevNam->getName() ); + uno::Sequence<rtl::OUString> aMemberNames( xMembers->getElementNames() ); + + aFieldNames.push_back( aFieldName ); + aFieldValues.push_back( aMemberNames ); + } + } + } + } + } + } + + // + // compare and build filters + // + + SCSIZE nDataFields = aDataNames.size(); + SCSIZE nFieldCount = aFieldNames.size(); + DBG_ASSERT( aGivenNames.size() == nDataFields && aFieldValues.size() == nFieldCount, "wrong count" ); + + bool bError = false; + bool bHasData = false; + String aRemaining( rFilterList ); + aRemaining.EraseLeadingAndTrailingChars( ' ' ); + while ( aRemaining.Len() && !bError ) + { + bool bUsed = false; + + // look for data field name + + for ( SCSIZE nDataPos=0; nDataPos<nDataFields && !bUsed; nDataPos++ ) + { + String aFound; + sal_Int32 nMatched = 0; + if ( lcl_IsAtStart( aRemaining, aDataNames[nDataPos], nMatched, false, NULL ) ) + aFound = aDataNames[nDataPos]; + else if ( lcl_IsAtStart( aRemaining, aGivenNames[nDataPos], nMatched, false, NULL ) ) + aFound = aGivenNames[nDataPos]; + + if ( aFound.Len() ) + { + rTarget.maFieldName = aFound; + aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) ); + bHasData = true; + bUsed = true; + } + } + + // look for field name + + String aSpecField; + bool bHasFieldName = false; + if ( !bUsed ) + { + sal_Int32 nMatched = 0; + for ( SCSIZE nField=0; nField<nFieldCount && !bHasFieldName; nField++ ) + { + if ( lcl_IsAtStart( aRemaining, aFieldNames[nField], nMatched, true, NULL ) ) + { + aSpecField = aFieldNames[nField]; + aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) ); + aRemaining.EraseLeadingChars( ' ' ); + + // field name has to be followed by item name in brackets + if ( aRemaining.GetChar(0) == '[' ) + { + bHasFieldName = true; + // bUsed remains false - still need the item + } + else + { + bUsed = true; + bError = true; + } + } + } + } + + // look for field item + + if ( !bUsed ) + { + bool bItemFound = false; + sal_Int32 nMatched = 0; + String aFoundName; + String aFoundValue; + sheet::GeneralFunction eFunc = sheet::GeneralFunction_NONE; + sheet::GeneralFunction eFoundFunc = sheet::GeneralFunction_NONE; + + for ( SCSIZE nField=0; nField<nFieldCount; nField++ ) + { + // If a field name is given, look in that field only, otherwise in all fields. + // aSpecField is initialized from aFieldNames array, so exact comparison can be used. + if ( !bHasFieldName || aFieldNames[nField] == aSpecField ) + { + const uno::Sequence<rtl::OUString>& rItems = aFieldValues[nField]; + sal_Int32 nItemCount = rItems.getLength(); + const rtl::OUString* pItemArr = rItems.getConstArray(); + for ( sal_Int32 nItem=0; nItem<nItemCount; nItem++ ) + { + if ( lcl_IsAtStart( aRemaining, pItemArr[nItem], nMatched, false, &eFunc ) ) + { + if ( bItemFound ) + bError = true; // duplicate (also across fields) + else + { + aFoundName = aFieldNames[nField]; + aFoundValue = pItemArr[nItem]; + eFoundFunc = eFunc; + bItemFound = true; + bUsed = true; + } + } + } + } + } + + if ( bItemFound && !bError ) + { + ScDPGetPivotDataField aField; + aField.maFieldName = aFoundName; + aField.meFunction = eFoundFunc; + aField.mbValIsStr = true; + aField.maValStr = aFoundValue; + aField.mnValNum = 0.0; + rFilters.push_back( aField ); + + aRemaining.Erase( 0, sal::static_int_cast<xub_StrLen>(nMatched) ); + } + } + + if ( !bUsed ) + bError = true; + + aRemaining.EraseLeadingChars( ' ' ); // remove any number of spaces between entries + } + + if ( !bError && !bHasData && aDataNames.size() == 1 ) + { + // if there's only one data field, its name need not be specified + rTarget.maFieldName = aDataNames[0]; + bHasData = true; + } + + return bHasData && !bError; +} + +void ScDPObject::ToggleDetails(const DataPilotTableHeaderData& rElemDesc, ScDPObject* pDestObj) +{ + CreateObjects(); // create xSource if not already done + + // find dimension name + + uno::Reference<container::XNamed> xDim; + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + long nIntCount = xIntDims->getCount(); + if ( rElemDesc.Dimension < nIntCount ) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( + xIntDims->getByIndex(rElemDesc.Dimension) ); + xDim = uno::Reference<container::XNamed>( xIntDim, uno::UNO_QUERY ); + } + DBG_ASSERT( xDim.is(), "dimension not found" ); + if ( !xDim.is() ) return; + String aDimName = xDim->getName(); + + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + BOOL bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + if (bDataLayout) + { + // the elements of the data layout dimension can't be found by their names + // -> don't change anything + return; + } + + // query old state + + long nHierCount = 0; + uno::Reference<container::XIndexAccess> xHiers; + uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies(); + xHiers = new ScNameToIndexAccess( xHiersName ); + nHierCount = xHiers->getCount(); + } + uno::Reference<uno::XInterface> xHier; + if ( rElemDesc.Hierarchy < nHierCount ) + xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex(rElemDesc.Hierarchy) ); + DBG_ASSERT( xHier.is(), "hierarchy not found" ); + if ( !xHier.is() ) return; + + long nLevCount = 0; + uno::Reference<container::XIndexAccess> xLevels; + uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY ); + if ( xLevSupp.is() ) + { + uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels(); + xLevels = new ScNameToIndexAccess( xLevsName ); + nLevCount = xLevels->getCount(); + } + uno::Reference<uno::XInterface> xLevel; + if ( rElemDesc.Level < nLevCount ) + xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex(rElemDesc.Level) ); + DBG_ASSERT( xLevel.is(), "level not found" ); + if ( !xLevel.is() ) return; + + uno::Reference<container::XNameAccess> xMembers; + uno::Reference<sheet::XMembersSupplier> xMbrSupp( xLevel, uno::UNO_QUERY ); + if ( xMbrSupp.is() ) + xMembers = xMbrSupp->getMembers(); + + BOOL bFound = FALSE; + BOOL bShowDetails = TRUE; + + if ( xMembers.is() ) + { + if ( xMembers->hasByName(rElemDesc.MemberName) ) + { + uno::Reference<uno::XInterface> xMemberInt = ScUnoHelpFunctions::AnyToInterface( + xMembers->getByName(rElemDesc.MemberName) ); + uno::Reference<beans::XPropertySet> xMbrProp( xMemberInt, uno::UNO_QUERY ); + if ( xMbrProp.is() ) + { + bShowDetails = ScUnoHelpFunctions::GetBoolProperty( xMbrProp, + rtl::OUString::createFromAscii(DP_PROP_SHOWDETAILS) ); + //! don't set bFound if property is unknown? + bFound = TRUE; + } + } + } + + DBG_ASSERT( bFound, "member not found" ); + + //! use Hierarchy and Level in SaveData !!!! + + // modify pDestObj if set, this object otherwise + ScDPSaveData* pModifyData = pDestObj ? ( pDestObj->pSaveData ) : pSaveData; + DBG_ASSERT( pModifyData, "no data?" ); + if ( pModifyData ) + { + const String aName = rElemDesc.MemberName; + pModifyData->GetDimensionByName(aDimName)-> + GetMemberByName(aName)->SetShowDetails( !bShowDetails ); // toggle + + if ( pDestObj ) + pDestObj->InvalidateData(); // re-init source from SaveData + else + InvalidateData(); // re-init source from SaveData + } +} + +long lcl_FindName( const rtl::OUString& rString, const uno::Reference<container::XNameAccess>& xCollection ) +{ + if ( xCollection.is() ) + { + uno::Sequence<rtl::OUString> aSeq = xCollection->getElementNames(); + long nCount = aSeq.getLength(); + const rtl::OUString* pArr = aSeq.getConstArray(); + for (long nPos=0; nPos<nCount; nPos++) + if ( pArr[nPos] == rString ) + return nPos; + } + return -1; // not found +} + +USHORT lcl_FirstSubTotal( const uno::Reference<beans::XPropertySet>& xDimProp ) // PIVOT_FUNC mask +{ + uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY ); + if ( xDimProp.is() && xDimSupp.is() ) + { + uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() ); + long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) ); + if ( nHierarchy >= xHiers->getCount() ) + nHierarchy = 0; + + uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface( + xHiers->getByIndex(nHierarchy) ); + uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() ); + uno::Reference<uno::XInterface> xLevel = + ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( 0 ) ); + uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY ); + if ( xLevProp.is() ) + { + uno::Any aSubAny; + try + { + aSubAny = xLevProp->getPropertyValue( + rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) ); + } + catch(uno::Exception&) + { + } + uno::Sequence<sheet::GeneralFunction> aSeq; + if ( aSubAny >>= aSeq ) + { + USHORT nMask = 0; + const sheet::GeneralFunction* pArray = aSeq.getConstArray(); + long nCount = aSeq.getLength(); + for (long i=0; i<nCount; i++) + nMask |= ScDataPilotConversion::FunctionBit(pArray[i]); + return nMask; + } + } + } + } + + DBG_ERROR("FirstSubTotal: NULL"); + return 0; +} + +USHORT lcl_CountBits( USHORT nBits ) +{ + if (!nBits) return 0; + + USHORT nCount = 0; + USHORT nMask = 1; + for (USHORT i=0; i<16; i++) + { + if ( nBits & nMask ) + ++nCount; + nMask <<= 1; + } + return nCount; +} + +SCSIZE lcl_FillOldFields( PivotField* pFields, + const uno::Reference<sheet::XDimensionsSupplier>& xSource, + USHORT nOrient, SCCOL nColAdd, BOOL bAddData ) +{ + SCSIZE nOutCount = 0; + BOOL bDataFound = FALSE; + + SCSIZE nCount = (nOrient == sheet::DataPilotFieldOrientation_PAGE) ? PIVOT_MAXPAGEFIELD : PIVOT_MAXFIELD; + + //! merge multiple occurences (data field with different functions) + //! force data field in one dimension + + std::vector< long > aPos( nCount, 0 ); + + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName ); + long nDimCount = xDims->getCount(); + for (long nDim=0; nDim < nDimCount && nOutCount < nCount; nDim++) + { + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + long nDimOrient = ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( xDimProp.is() && nDimOrient == nOrient ) + { + USHORT nMask = 0; + if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) + { + sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION), + sheet::GeneralFunction_NONE ); + if ( eFunc == sheet::GeneralFunction_AUTO ) + { + //! test for numeric data + eFunc = sheet::GeneralFunction_SUM; + } + nMask = ScDataPilotConversion::FunctionBit(eFunc); + } + else + nMask = lcl_FirstSubTotal( xDimProp ); // from first hierarchy + + BOOL bDataLayout = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + uno::Any aOrigAny; + try + { + aOrigAny = xDimProp->getPropertyValue( + rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) ); + } + catch(uno::Exception&) + { + } + + long nDupSource = -1; + uno::Reference<uno::XInterface> xIntOrig = ScUnoHelpFunctions::AnyToInterface( aOrigAny ); + if ( xIntOrig.is() ) + { + uno::Reference<container::XNamed> xNameOrig( xIntOrig, uno::UNO_QUERY ); + if ( xNameOrig.is() ) + nDupSource = lcl_FindName( xNameOrig->getName(), xDimsName ); + } + + BOOL bDupUsed = FALSE; + if ( nDupSource >= 0 ) + { + // add function bit to previous entry + + SCsCOL nCompCol; + if ( bDataLayout ) + nCompCol = PIVOT_DATA_FIELD; + else + nCompCol = static_cast<SCsCOL>(nDupSource)+nColAdd; //! seek source column from name + + for (SCSIZE nOld=0; nOld<nOutCount && !bDupUsed; nOld++) + if ( pFields[nOld].nCol == nCompCol ) + { + // add to previous column only if new bits aren't already set there + if ( ( pFields[nOld].nFuncMask & nMask ) == 0 ) + { + pFields[nOld].nFuncMask |= nMask; + pFields[nOld].nFuncCount = lcl_CountBits( pFields[nOld].nFuncMask ); + bDupUsed = TRUE; + } + } + } + + if ( !bDupUsed ) // also for duplicated dim if original has different orientation + { + if ( bDataLayout ) + { + pFields[nOutCount].nCol = PIVOT_DATA_FIELD; + bDataFound = TRUE; + } + else if ( nDupSource >= 0 ) // if source was not found (different orientation) + pFields[nOutCount].nCol = static_cast<SCsCOL>(nDupSource)+nColAdd; //! seek from name + else + pFields[nOutCount].nCol = static_cast<SCsCOL>(nDim)+nColAdd; //! seek source column from name + + pFields[nOutCount].nFuncMask = nMask; + pFields[nOutCount].nFuncCount = lcl_CountBits( nMask ); + aPos[nOutCount] = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_POSITION) ); + + try + { + if( nOrient == sheet::DataPilotFieldOrientation_DATA ) + xDimProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_REFVALUE ) ) ) + >>= pFields[nOutCount].maFieldRef; + } + catch( uno::Exception& ) + { + } + + ++nOutCount; + } + } + } + + // sort by getPosition() value + + for (SCSIZE i=0; i+1<nOutCount; i++) + { + for (SCSIZE j=0; j+i+1<nOutCount; j++) + if ( aPos[j+1] < aPos[j] ) + { + std::swap( aPos[j], aPos[j+1] ); + std::swap( pFields[j], pFields[j+1] ); + } + } + + if ( bAddData && !bDataFound ) + { + if ( nOutCount >= nCount ) // space for data field? + --nOutCount; //! error? + pFields[nOutCount].nCol = PIVOT_DATA_FIELD; + pFields[nOutCount].nFuncMask = 0; + pFields[nOutCount].nFuncCount = 0; + ++nOutCount; + } + + return nOutCount; +} + +BOOL ScDPObject::FillOldParam(ScPivotParam& rParam, BOOL bForFile) const +{ + ((ScDPObject*)this)->CreateObjects(); // xSource is needed for field numbers + + rParam.nCol = aOutRange.aStart.Col(); + rParam.nRow = aOutRange.aStart.Row(); + rParam.nTab = aOutRange.aStart.Tab(); + // ppLabelArr / nLabels is not changed + + SCCOL nColAdd = 0; + if ( bForFile ) + { + // in old file format, columns are within document, not within source range + + DBG_ASSERT( pSheetDesc, "FillOldParam: bForFile, !pSheetDesc" ); + nColAdd = pSheetDesc->aSourceRange.aStart.Col(); + } + + BOOL bAddData = ( lcl_GetDataGetOrientation( xSource ) == sheet::DataPilotFieldOrientation_HIDDEN ); + rParam.nPageCount = lcl_FillOldFields( rParam.aPageArr, + xSource, sheet::DataPilotFieldOrientation_PAGE, nColAdd, FALSE ); + rParam.nColCount = lcl_FillOldFields( rParam.aColArr, + xSource, sheet::DataPilotFieldOrientation_COLUMN, nColAdd, bAddData ); + rParam.nRowCount = lcl_FillOldFields( rParam.aRowArr, + xSource, sheet::DataPilotFieldOrientation_ROW, nColAdd, FALSE ); + rParam.nDataCount = lcl_FillOldFields( rParam.aDataArr, + xSource, sheet::DataPilotFieldOrientation_DATA, nColAdd, FALSE ); + + uno::Reference<beans::XPropertySet> xProp( xSource, uno::UNO_QUERY ); + if (xProp.is()) + { + try + { + rParam.bMakeTotalCol = ScUnoHelpFunctions::GetBoolProperty( xProp, + rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND), TRUE ); + rParam.bMakeTotalRow = ScUnoHelpFunctions::GetBoolProperty( xProp, + rtl::OUString::createFromAscii(DP_PROP_ROWGRAND), TRUE ); + + // following properties may be missing for external sources + rParam.bIgnoreEmptyRows = ScUnoHelpFunctions::GetBoolProperty( xProp, + rtl::OUString::createFromAscii(DP_PROP_IGNOREEMPTY) ); + rParam.bDetectCategories = ScUnoHelpFunctions::GetBoolProperty( xProp, + rtl::OUString::createFromAscii(DP_PROP_REPEATIFEMPTY) ); + } + catch(uno::Exception&) + { + // no error + } + } + return TRUE; +} + +void lcl_FillLabelData( LabelData& rData, const uno::Reference< beans::XPropertySet >& xDimProp ) +{ + uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDimProp, uno::UNO_QUERY ); + if ( xDimProp.is() && xDimSupp.is() ) + { + uno::Reference<container::XIndexAccess> xHiers = new ScNameToIndexAccess( xDimSupp->getHierarchies() ); + long nHierarchy = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) ); + if ( nHierarchy >= xHiers->getCount() ) + nHierarchy = 0; + rData.mnUsedHier = nHierarchy; + + uno::Reference<uno::XInterface> xHier = ScUnoHelpFunctions::AnyToInterface( + xHiers->getByIndex(nHierarchy) ); + + uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XIndexAccess> xLevels = new ScNameToIndexAccess( xHierSupp->getLevels() ); + uno::Reference<uno::XInterface> xLevel = + ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( 0 ) ); + uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY ); + if ( xLevProp.is() ) + { + rData.mbShowAll = ScUnoHelpFunctions::GetBoolProperty( xLevProp, + rtl::OUString::createFromAscii(DP_PROP_SHOWEMPTY) ); + + try + { + xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_SORTING ) ) ) + >>= rData.maSortInfo; + xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_LAYOUT ) ) ) + >>= rData.maLayoutInfo; + xLevProp->getPropertyValue( rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_AUTOSHOW ) ) ) + >>= rData.maShowInfo; + } + catch(uno::Exception&) + { + } + } + } + } +} + +BOOL ScDPObject::FillLabelData(ScPivotParam& rParam) +{ + ((ScDPObject*)this)->CreateObjects(); + + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName ); + long nDimCount = xDims->getCount(); + if ( nDimCount > MAX_LABELS ) + nDimCount = MAX_LABELS; + if (!nDimCount) + return FALSE; + + SCSIZE nOutCount = 0; + LabelData** aLabelArr = new LabelData*[nDimCount]; + for (long nDim=0; nDim < nDimCount; nDim++) + { + String aFieldName; + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + + if ( xDimName.is() && xDimProp.is() ) + { + BOOL bDuplicated = FALSE; + BOOL bData = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + //! error checking -- is "IsDataLayoutDimension" property required?? + + try + { + aFieldName = String( xDimName->getName() ); + + uno::Any aOrigAny = xDimProp->getPropertyValue( + rtl::OUString::createFromAscii(DP_PROP_ORIGINAL) ); + uno::Reference<uno::XInterface> xIntOrig; + if ( (aOrigAny >>= xIntOrig) && xIntOrig.is() ) + bDuplicated = TRUE; + } + catch(uno::Exception&) + { + } + + if ( aFieldName.Len() && !bData && !bDuplicated ) + { + SCsCOL nCol = static_cast< SCsCOL >( nDim ); //! ??? + bool bIsValue = true; //! check + + aLabelArr[nOutCount] = new LabelData( aFieldName, nCol, bIsValue ); + + LabelData& rLabelData = *aLabelArr[nOutCount]; + GetHierarchies( nDim, rLabelData.maHiers ); + GetMembers( nDim, rLabelData.maMembers, &rLabelData.maVisible, &rLabelData.maShowDet ); + lcl_FillLabelData( rLabelData, xDimProp ); + + ++nOutCount; + } + } + } + + rParam.SetLabelData( aLabelArr, nOutCount ); + + for (SCSIZE i=0; i<nOutCount; i++) + delete aLabelArr[i]; + delete[] aLabelArr; + + return TRUE; +} + +BOOL ScDPObject::GetHierarchiesNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xHiers ) +{ + BOOL bRet = FALSE; + uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() ); + uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName )); + if( xIntDims.is() ) + { + uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xIntDims->getByIndex( nDim ), uno::UNO_QUERY); + if (xHierSup.is()) + { + xHiers.set( xHierSup->getHierarchies() ); + bRet = xHiers.is(); + } + } + return bRet; +} + +BOOL ScDPObject::GetHierarchies( sal_Int32 nDim, uno::Sequence< rtl::OUString >& rHiers ) +{ + BOOL bRet = FALSE; + uno::Reference< container::XNameAccess > xHiersNA; + if( GetHierarchiesNA( nDim, xHiersNA ) ) + { + rHiers = xHiersNA->getElementNames(); + bRet = TRUE; + } + return bRet; +} + +sal_Int32 ScDPObject::GetUsedHierarchy( sal_Int32 nDim ) +{ + sal_Int32 nHier = 0; + uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() ); + uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName )); + uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY); + if (xDim.is()) + nHier = ScUnoHelpFunctions::GetLongProperty( xDim, rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( SC_UNO_USEDHIER ) ) ); + return nHier; +} + +BOOL ScDPObject::GetMembersNA( sal_Int32 nDim, uno::Reference< container::XNameAccess >& xMembers ) +{ + return GetMembersNA( nDim, GetUsedHierarchy( nDim ), xMembers ); +} + +BOOL ScDPObject::GetMembers( sal_Int32 nDim, + uno::Sequence< rtl::OUString >& rMembers, + uno::Sequence< sal_Bool >* pVisible, + uno::Sequence< sal_Bool >* pShowDet ) +{ + return GetMembers( nDim, GetUsedHierarchy( nDim ), rMembers, pVisible, pShowDet ); +} + +BOOL ScDPObject::GetMembersNA( sal_Int32 nDim, sal_Int32 nHier, uno::Reference< container::XNameAccess >& xMembers ) +{ + BOOL bRet = FALSE; + uno::Reference<container::XNameAccess> xDimsName( GetSource()->getDimensions() ); + uno::Reference<container::XIndexAccess> xIntDims(new ScNameToIndexAccess( xDimsName )); + uno::Reference<beans::XPropertySet> xDim(xIntDims->getByIndex( nDim ), uno::UNO_QUERY); + if (xDim.is()) + { + uno::Reference<sheet::XHierarchiesSupplier> xHierSup(xDim, uno::UNO_QUERY); + if (xHierSup.is()) + { + uno::Reference<container::XIndexAccess> xHiers(new ScNameToIndexAccess(xHierSup->getHierarchies())); + uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHiers->getByIndex(nHier), uno::UNO_QUERY ); + if ( xLevSupp.is() ) + { + uno::Reference<container::XIndexAccess> xLevels(new ScNameToIndexAccess( xLevSupp->getLevels())); + if (xLevels.is()) + { + sal_Int32 nLevCount = xLevels->getCount(); + if (nLevCount > 0) + { + uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevels->getByIndex(0), uno::UNO_QUERY ); + if ( xMembSupp.is() ) + { + xMembers.set(xMembSupp->getMembers()); + bRet = TRUE; + } + } + } + } + } + } + return bRet; +} + +BOOL ScDPObject::GetMembers( sal_Int32 nDim, sal_Int32 nHier, + uno::Sequence< rtl::OUString >& rMembers, + uno::Sequence< sal_Bool >* pVisible, + uno::Sequence< sal_Bool >* pShowDet ) +{ + BOOL bRet = FALSE; + uno::Reference< container::XNameAccess > xMembersNA; + if( GetMembersNA( nDim, nHier, xMembersNA ) ) + { + uno::Reference< container::XIndexAccess > xMembersIA( new ScNameToIndexAccess( xMembersNA ) ); + sal_Int32 nCount = xMembersIA->getCount(); + rMembers.realloc( nCount ); + if( pVisible ) + pVisible->realloc( nCount ); + if( pShowDet ) + pShowDet->realloc( nCount ); + + rtl::OUString* pAry = rMembers.getArray(); + for( sal_Int32 nItem = 0; nItem < nCount; ++nItem ) + { + uno::Reference< container::XNamed > xMember( xMembersIA->getByIndex( nItem ), uno::UNO_QUERY ); + if( xMember.is() ) + pAry[ nItem ] = xMember->getName(); + if( pVisible || pShowDet ) + { + uno::Reference< beans::XPropertySet > xMemProp( xMember, uno::UNO_QUERY ); + if( pVisible ) + { + sal_Bool bVis = sal_True; + if( xMemProp.is() ) + bVis = ScUnoHelpFunctions::GetBoolProperty( xMemProp, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_ISVISIBL ) ) ); + (*pVisible)[ nItem ] = bVis; + } + if( pShowDet ) + { + sal_Bool bShow = sal_True; + if( xMemProp.is() ) + bShow = ScUnoHelpFunctions::GetBoolProperty( xMemProp, + rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( SC_UNO_SHOWDETA ) ) ); + (*pShowDet)[ nItem ] = bShow; + } + } + } + bRet = TRUE; + } + return bRet; +} + +//------------------------------------------------------------------------ +// convert old pivot tables into new datapilot tables + +String lcl_GetDimName( const uno::Reference<sheet::XDimensionsSupplier>& xSource, long nDim ) +{ + rtl::OUString aName; + if ( xSource.is() ) + { + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xDimsName ); + long nDimCount = xDims->getCount(); + if ( nDim < nDimCount ) + { + uno::Reference<uno::XInterface> xIntDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY ); + if (xDimName.is()) + { + try + { + aName = xDimName->getName(); + } + catch(uno::Exception&) + { + } + } + } + } + return aName; +} + +// static +void ScDPObject::ConvertOrientation( ScDPSaveData& rSaveData, + PivotField* pFields, SCSIZE nCount, USHORT nOrient, + ScDocument* pDoc, SCROW nRow, SCTAB nTab, + const uno::Reference<sheet::XDimensionsSupplier>& xSource, + BOOL bOldDefaults, + PivotField* pRefColFields, SCSIZE nRefColCount, + PivotField* pRefRowFields, SCSIZE nRefRowCount, + PivotField* pRefPageFields, SCSIZE nRefPageCount ) +{ + // pDoc or xSource must be set + DBG_ASSERT( pDoc || xSource.is(), "missing string source" ); + + String aDocStr; + ScDPSaveDimension* pDim; + + for (SCSIZE i=0; i<nCount; i++) + { + SCCOL nCol = pFields[i].nCol; + USHORT nFuncs = pFields[i].nFuncMask; + const sheet::DataPilotFieldReference& rFieldRef = pFields[i].maFieldRef; + + if ( nCol == PIVOT_DATA_FIELD ) + pDim = rSaveData.GetDataLayoutDimension(); + else + { + if ( pDoc ) + pDoc->GetString( nCol, nRow, nTab, aDocStr ); + else + aDocStr = lcl_GetDimName( xSource, nCol ); // cols must start at 0 + + if ( aDocStr.Len() ) + pDim = rSaveData.GetDimensionByName(aDocStr); + else + pDim = NULL; + } + + if ( pDim ) + { + if ( nOrient == sheet::DataPilotFieldOrientation_DATA ) // set summary function + { + // generate an individual entry for each function + BOOL bFirst = TRUE; + + // if a dimension is used for column/row/page and data, + // use duplicated dimensions for all data occurrences + if (pRefColFields) + for (SCSIZE nRefCol=0; nRefCol<nRefColCount; nRefCol++) + if (pRefColFields[nRefCol].nCol == nCol) + bFirst = FALSE; + if (pRefRowFields) + for (SCSIZE nRefRow=0; nRefRow<nRefRowCount; nRefRow++) + if (pRefRowFields[nRefRow].nCol == nCol) + bFirst = FALSE; + if (pRefPageFields) + for (USHORT nRefPage=0; nRefPage<nRefPageCount; ++nRefPage) + if (pRefPageFields[nRefPage].nCol == nCol) + bFirst = FALSE; + + // if set via api, a data column may occur several times + // (if the function hasn't been changed yet) -> also look for duplicate data column + for (SCSIZE nPrevData=0; nPrevData<i; nPrevData++) + if (pFields[nPrevData].nCol == nCol) + bFirst = FALSE; + + USHORT nMask = 1; + for (USHORT nBit=0; nBit<16; nBit++) + { + if ( nFuncs & nMask ) + { + sheet::GeneralFunction eFunc = ScDataPilotConversion::FirstFunc( nMask ); + ScDPSaveDimension* pCurrDim = bFirst ? pDim : rSaveData.DuplicateDimension(pDim->GetName()); + pCurrDim->SetOrientation( nOrient ); + pCurrDim->SetFunction( sal::static_int_cast<USHORT>(eFunc) ); + + if( rFieldRef.ReferenceType == sheet::DataPilotFieldReferenceType::NONE ) + pCurrDim->SetReferenceValue( 0 ); + else + pCurrDim->SetReferenceValue( &rFieldRef ); + + bFirst = FALSE; + } + nMask *= 2; + } + } + else // set SubTotals + { + pDim->SetOrientation( nOrient ); + + USHORT nFuncArray[16]; + USHORT nFuncCount = 0; + USHORT nMask = 1; + for (USHORT nBit=0; nBit<16; nBit++) + { + if ( nFuncs & nMask ) + nFuncArray[nFuncCount++] = sal::static_int_cast<USHORT>(ScDataPilotConversion::FirstFunc( nMask )); + nMask *= 2; + } + pDim->SetSubTotals( nFuncCount, nFuncArray ); + + // ShowEmpty was implicit in old tables, + // must be set for data layout dimension (not accessible in dialog) + if ( bOldDefaults || nCol == PIVOT_DATA_FIELD ) + pDim->SetShowEmpty( TRUE ); + } + } + } +} + +// ----------------------------------------------------------------------- + +// static +BOOL ScDPObject::HasRegisteredSources() +{ + BOOL bFound = FALSE; + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY ); + if ( xEnAc.is() ) + { + uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration( + rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) ); + if ( xEnum.is() && xEnum->hasMoreElements() ) + bFound = TRUE; + } + + return bFound; +} + +// static +uno::Sequence<rtl::OUString> ScDPObject::GetRegisteredSources() +{ + long nCount = 0; + uno::Sequence<rtl::OUString> aSeq(0); + + // use implementation names... + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY ); + if ( xEnAc.is() ) + { + uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration( + rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) ); + if ( xEnum.is() ) + { + while ( xEnum->hasMoreElements() ) + { + uno::Any aAddInAny = xEnum->nextElement(); +// if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE ) + { + uno::Reference<uno::XInterface> xIntFac; + aAddInAny >>= xIntFac; + if ( xIntFac.is() ) + { + uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY ); + if ( xInfo.is() ) + { + rtl::OUString sName = xInfo->getImplementationName(); + + aSeq.realloc( nCount+1 ); + aSeq.getArray()[nCount] = sName; + ++nCount; + } + } + } + } + } + } + + return aSeq; +} + +// static +uno::Reference<sheet::XDimensionsSupplier> ScDPObject::CreateSource( const ScDPServiceDesc& rDesc ) +{ + rtl::OUString aImplName = rDesc.aServiceName; + uno::Reference<sheet::XDimensionsSupplier> xRet = NULL; + + uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory(); + uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY ); + if ( xEnAc.is() ) + { + uno::Reference<container::XEnumeration> xEnum = xEnAc->createContentEnumeration( + rtl::OUString::createFromAscii( SCDPSOURCE_SERVICE ) ); + if ( xEnum.is() ) + { + while ( xEnum->hasMoreElements() && !xRet.is() ) + { + uno::Any aAddInAny = xEnum->nextElement(); +// if ( aAddInAny.getReflection()->getTypeClass() == TypeClass_INTERFACE ) + { + uno::Reference<uno::XInterface> xIntFac; + aAddInAny >>= xIntFac; + if ( xIntFac.is() ) + { + uno::Reference<lang::XServiceInfo> xInfo( xIntFac, uno::UNO_QUERY ); + uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY ); + if ( xFac.is() && xInfo.is() && xInfo->getImplementationName() == aImplName ) + { + try + { + uno::Reference<uno::XInterface> xInterface = xFac->createInstance(); + uno::Reference<lang::XInitialization> xInit( xInterface, uno::UNO_QUERY ); + if (xInit.is()) + { + // initialize + uno::Sequence<uno::Any> aSeq(4); + uno::Any* pArray = aSeq.getArray(); + pArray[0] <<= rtl::OUString( rDesc.aParSource ); + pArray[1] <<= rtl::OUString( rDesc.aParName ); + pArray[2] <<= rtl::OUString( rDesc.aParUser ); + pArray[3] <<= rtl::OUString( rDesc.aParPass ); + xInit->initialize( aSeq ); + } + xRet = uno::Reference<sheet::XDimensionsSupplier>( xInterface, uno::UNO_QUERY ); + } + catch(uno::Exception&) + { + } + } + } + } + } + } + } + + return xRet; +} + +// ============================================================================ + +ScDPCacheCell::ScDPCacheCell() : + mnStrId(ScSimpleSharedString::EMPTY), + mnType(SC_VALTYPE_EMPTY), + mfValue(0.0), + mbNumeric(false) +{ +} + +ScDPCacheCell::ScDPCacheCell(const ScDPCacheCell& r) : + mnStrId(r.mnStrId), + mnType(r.mnType), + mfValue(r.mfValue), + mbNumeric(r.mbNumeric) +{ +} + +ScDPCacheCell::~ScDPCacheCell() +{ +} + +// ============================================================================ + +size_t ScDPCollection::CacheCellHash::operator()(const ScDPCacheCell* pCell) const +{ + return pCell->mnStrId + static_cast<size_t>(pCell->mnType) + + static_cast<size_t>(pCell->mfValue) + static_cast<size_t>(pCell->mbNumeric); +} + +bool ScDPCollection::CacheCellEqual::operator()(const ScDPCacheCell* p1, const ScDPCacheCell* p2) const +{ + if (!p1 && !p2) + return true; + + if ((!p1 && p2) || (p1 && !p2)) + return false; + + return p1->mnStrId == p2->mnStrId && p1->mfValue == p2->mfValue && + p1->mbNumeric == p2->mbNumeric && p1->mnType == p2->mnType; +} + +// ---------------------------------------------------------------------------- + +ScDPCollection::ScDPCollection(ScDocument* pDocument) : + pDoc( pDocument ) +{ +} + +ScDPCollection::ScDPCollection(const ScDPCollection& r) : + ScCollection(r), + pDoc(r.pDoc), + maSharedString(r.maSharedString), + maCacheCellPool() // #i101725# don't copy hash_set with pointers from the other collection +{ +} + +ScDPCollection::~ScDPCollection() +{ + clearCacheCellPool(); +} + +ScDataObject* ScDPCollection::Clone() const +{ + return new ScDPCollection(*this); +} + +void ScDPCollection::DeleteOnTab( SCTAB nTab ) +{ + USHORT nPos = 0; + while ( nPos < nCount ) + { + // look for output positions on the deleted sheet + if ( static_cast<const ScDPObject*>(At(nPos))->GetOutRange().aStart.Tab() == nTab ) + AtFree(nPos); + else + ++nPos; + } +} + +void ScDPCollection::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& r, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + for (USHORT i=0; i<nCount; i++) + ((ScDPObject*)At(i))->UpdateReference( eUpdateRefMode, r, nDx, nDy, nDz ); +} + +BOOL ScDPCollection::RefsEqual( const ScDPCollection& r ) const +{ + if ( nCount != r.nCount ) + return FALSE; + + for (USHORT i=0; i<nCount; i++) + if ( ! ((const ScDPObject*)At(i))->RefsEqual( *((const ScDPObject*)r.At(i)) ) ) + return FALSE; + + return TRUE; // all equal +} + +void ScDPCollection::WriteRefsTo( ScDPCollection& r ) const +{ + if ( nCount == r.nCount ) + { + //! assert equal names? + for (USHORT i=0; i<nCount; i++) + ((const ScDPObject*)At(i))->WriteRefsTo( *((ScDPObject*)r.At(i)) ); + } + else + { + // #i8180# If data pilot tables were deleted with their sheet, + // this collection contains extra entries that must be restored. + // Matching objects are found by their names. + + DBG_ASSERT( nCount >= r.nCount, "WriteRefsTo: missing entries in document" ); + for (USHORT nSourcePos=0; nSourcePos<nCount; nSourcePos++) + { + const ScDPObject* pSourceObj = static_cast<const ScDPObject*>(At(nSourcePos)); + String aName = pSourceObj->GetName(); + bool bFound = false; + for (USHORT nDestPos=0; nDestPos<r.nCount && !bFound; nDestPos++) + { + ScDPObject* pDestObj = static_cast<ScDPObject*>(r.At(nDestPos)); + if ( pDestObj->GetName() == aName ) + { + pSourceObj->WriteRefsTo( *pDestObj ); // found object, copy refs + bFound = true; + } + } + if ( !bFound ) + { + // none found, re-insert deleted object (see ScUndoDataPilot::Undo) + + ScDPObject* pDestObj = new ScDPObject( *pSourceObj ); + pDestObj->SetAlive(TRUE); + if ( !r.Insert(pDestObj) ) + { + DBG_ERROR("cannot insert DPObject"); + DELETEZ( pDestObj ); + } + } + } + DBG_ASSERT( nCount == r.nCount, "WriteRefsTo: couldn't restore all entries" ); + } +} + +String ScDPCollection::CreateNewName( USHORT nMin ) const +{ + String aBase = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("DataPilot")); + //! from Resource? + + for (USHORT nAdd=0; nAdd<=nCount; nAdd++) // nCount+1 tries + { + String aNewName = aBase; + aNewName += String::CreateFromInt32( nMin + nAdd ); + BOOL bFound = FALSE; + for (USHORT i=0; i<nCount && !bFound; i++) + if (((const ScDPObject*)pItems[i])->GetName() == aNewName) + bFound = TRUE; + if (!bFound) + return aNewName; // found unused Name + } + return String(); // should not happen +} + +ScSimpleSharedString& ScDPCollection::GetSharedString() +{ + return maSharedString; +} + +ScDPCacheCell* ScDPCollection::getCacheCellFromPool(const ScDPCacheCell& rCell) +{ + ScDPCacheCell aCell(rCell); + CacheCellPoolType::iterator itr = maCacheCellPool.find(&aCell); + if (itr == maCacheCellPool.end()) + { + // Insert a new instance. + ScDPCacheCell* p = new ScDPCacheCell(rCell); + ::std::pair<CacheCellPoolType::iterator, bool> r = + maCacheCellPool.insert(p); + if (!r.second) + delete p; + + ScDPCacheCell* p2 = r.second ? *r.first : NULL; + DBG_ASSERT(p == p2, "ScDPCollection::getCacheCellFromPool: pointer addresses differ"); + return p2; + } + return *itr; +} + +namespace { + +class DeleteCacheCells : public ::std::unary_function<ScDPCacheCell*, void> +{ +public: + void operator()(ScDPCacheCell* p) const + { + delete p; + } +}; + +} + +void ScDPCollection::clearCacheCellPool() +{ + // Transferring all stored pointers to a vector first. For some unknown + // reason, deleting cell content instances by directly iterating through + // the hash set causes the iteration to return an identical pointer + // value twice, causing a double-delete. I have no idea why this happens. + + using ::std::copy; + using ::std::back_inserter; + + vector<ScDPCacheCell*> ps; + ps.reserve(maCacheCellPool.size()); + copy(maCacheCellPool.begin(), maCacheCellPool.end(), back_inserter(ps)); + maCacheCellPool.clear(); + // for correctness' sake, delete the elements after clearing the hash_set + for_each(ps.begin(), ps.end(), DeleteCacheCells()); +} + diff --git a/sc/source/core/data/dpoutput.cxx b/sc/source/core/data/dpoutput.cxx new file mode 100644 index 000000000000..d1fad68f16a2 --- /dev/null +++ b/sc/source/core/data/dpoutput.cxx @@ -0,0 +1,1961 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpoutput.cxx,v $ + * $Revision: 1.17.30.2 $ + * + * 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 "scitems.hxx" +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/wghtitem.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "dpoutput.hxx" +#include "dptabsrc.hxx" +#include "dpcachetable.hxx" +#include "document.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "markdata.hxx" +#include "attrib.hxx" +#include "formula/errorcodes.hxx" // errNoValue +#include "miscuno.hxx" +#include "globstr.hrc" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "collect.hxx" +#include "scresid.hxx" +#include "unonames.hxx" +#include "sc.hrc" + +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotTableHeaderData.hpp> +#include <com/sun/star/sheet/DataPilotTablePositionData.hpp> +#include <com/sun/star/sheet/DataPilotTablePositionType.hpp> +#include <com/sun/star/sheet/DataPilotTableResultData.hpp> +#include <com/sun/star/sheet/DataResultFlags.hpp> +#include <com/sun/star/sheet/GeneralFunction.hpp> +#include <com/sun/star/sheet/MemberResultFlags.hpp> +#include <com/sun/star/sheet/TableFilterField.hpp> +#include <com/sun/star/sheet/XDataPilotMemberResults.hpp> +#include <com/sun/star/sheet/XDataPilotResults.hpp> +#include <com/sun/star/sheet/XHierarchiesSupplier.hpp> +#include <com/sun/star/sheet/XLevelsSupplier.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> + +#include <vector> + +using namespace com::sun::star; +using ::std::vector; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::UNO_QUERY; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::sheet::DataPilotTablePositionData; +using ::com::sun::star::sheet::DataPilotTableResultData; +using ::com::sun::star::uno::makeAny; +using ::com::sun::star::uno::Any; +using ::rtl::OUString; + +// ----------------------------------------------------------------------- + +//! move to a header file +//! use names from unonames.hxx? +#define DP_PROP_FUNCTION "Function" +#define DP_PROP_ORIENTATION "Orientation" +#define DP_PROP_POSITION "Position" +#define DP_PROP_USEDHIERARCHY "UsedHierarchy" +#define DP_PROP_DATADESCR "DataDescription" +#define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension" +#define DP_PROP_NUMBERFORMAT "NumberFormat" +#define DP_PROP_FILTER "Filter" +#define DP_PROP_COLUMNGRAND "ColumnGrand" +#define DP_PROP_ROWGRAND "RowGrand" +#define DP_PROP_SUBTOTALS "SubTotals" + +// ----------------------------------------------------------------------- + +//! dynamic!!! +#define SC_DPOUT_MAXLEVELS 256 + + +struct ScDPOutLevelData +{ + long nDim; + long nHier; + long nLevel; + long nDimPos; + uno::Sequence<sheet::MemberResult> aResult; + String aCaption; + + ScDPOutLevelData() { nDim = nHier = nLevel = nDimPos = -1; } + + BOOL operator<(const ScDPOutLevelData& r) const + { return nDimPos<r.nDimPos || ( nDimPos==r.nDimPos && nHier<r.nHier ) || + ( nDimPos==r.nDimPos && nHier==r.nHier && nLevel<r.nLevel ); } + + void Swap(ScDPOutLevelData& r) +//! { ScDPOutLevelData aTemp = r; r = *this; *this = aTemp; } + { ScDPOutLevelData aTemp; aTemp = r; r = *this; *this = aTemp; } + + //! bug (73840) in uno::Sequence - copy and then assign doesn't work! +}; + +// ----------------------------------------------------------------------- + +void lcl_SetStyleById( ScDocument* pDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nStrId ) +{ + if ( nCol1 > nCol2 || nRow1 > nRow2 ) + { + DBG_ERROR("SetStyleById: invalid range"); + return; + } + + String aStyleName = ScGlobal::GetRscString( nStrId ); + ScStyleSheetPool* pStlPool = pDoc->GetStyleSheetPool(); + ScStyleSheet* pStyle = (ScStyleSheet*) pStlPool->Find( aStyleName, SFX_STYLE_FAMILY_PARA ); + if (!pStyle) + { + // create new style (was in ScPivot::SetStyle) + + pStyle = (ScStyleSheet*) &pStlPool->Make( aStyleName, SFX_STYLE_FAMILY_PARA, + SFXSTYLEBIT_USERDEF ); + pStyle->SetParent( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ); + SfxItemSet& rSet = pStyle->GetItemSet(); + if ( nStrId==STR_PIVOT_STYLE_RESULT || nStrId==STR_PIVOT_STYLE_TITLE ) + rSet.Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) ); + if ( nStrId==STR_PIVOT_STYLE_CATEGORY || nStrId==STR_PIVOT_STYLE_TITLE ) + rSet.Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_LEFT, ATTR_HOR_JUSTIFY ) ); + } + + pDoc->ApplyStyleAreaTab( nCol1, nRow1, nCol2, nRow2, nTab, *pStyle ); +} + +void lcl_SetFrame( ScDocument* pDoc, SCTAB nTab, + SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nWidth ) +{ + SvxBorderLine aLine; + aLine.SetOutWidth(nWidth); + SvxBoxItem aBox( ATTR_BORDER ); + aBox.SetLine(&aLine, BOX_LINE_LEFT); + aBox.SetLine(&aLine, BOX_LINE_TOP); + aBox.SetLine(&aLine, BOX_LINE_RIGHT); + aBox.SetLine(&aLine, BOX_LINE_BOTTOM); + SvxBoxInfoItem aBoxInfo( ATTR_BORDER_INNER ); + aBoxInfo.SetValid(VALID_HORI,FALSE); + aBoxInfo.SetValid(VALID_VERT,FALSE); + aBoxInfo.SetValid(VALID_DISTANCE,FALSE); + + pDoc->ApplyFrameAreaTab( ScRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab ), &aBox, &aBoxInfo ); +} + +// ----------------------------------------------------------------------- + +void lcl_FillNumberFormats( UINT32*& rFormats, long& rCount, + const uno::Reference<sheet::XDataPilotMemberResults>& xLevRes, + const uno::Reference<container::XIndexAccess>& xDims ) +{ + if ( rFormats ) + return; // already set + + // xLevRes is from the data layout dimension + //! use result sequence from ScDPOutLevelData! + + uno::Sequence<sheet::MemberResult> aResult = xLevRes->getResults(); + + long nSize = aResult.getLength(); + if (nSize) + { + // get names/formats for all data dimensions + //! merge this with the loop to collect ScDPOutLevelData? + + String aDataNames[SC_DPOUT_MAXLEVELS]; + UINT32 nDataFormats[SC_DPOUT_MAXLEVELS]; + long nDataCount = 0; + BOOL bAnySet = FALSE; + + long nDimCount = xDims->getCount(); + for (long nDim=0; nDim<nDimCount; nDim++) + { + uno::Reference<uno::XInterface> xDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY ); + if ( xDimProp.is() && xDimName.is() ) + { + sheet::DataPilotFieldOrientation eDimOrient = + (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA ) + { + aDataNames[nDataCount] = String( xDimName->getName() ); + long nFormat = ScUnoHelpFunctions::GetLongProperty( + xDimProp, + rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) ); + nDataFormats[nDataCount] = nFormat; + if ( nFormat != 0 ) + bAnySet = TRUE; + ++nDataCount; + } + } + } + + if ( bAnySet ) // forget everything if all formats are 0 (or no data dimensions) + { + const sheet::MemberResult* pArray = aResult.getConstArray(); + + String aName; + UINT32* pNumFmt = new UINT32[nSize]; + if (nDataCount == 1) + { + // only one data dimension -> use its numberformat everywhere + long nFormat = nDataFormats[0]; + for (long nPos=0; nPos<nSize; nPos++) + pNumFmt[nPos] = nFormat; + } + else + { + for (long nPos=0; nPos<nSize; nPos++) + { + // if CONTINUE bit is set, keep previous name + //! keep number format instead! + if ( !(pArray[nPos].Flags & sheet::MemberResultFlags::CONTINUE) ) + aName = String( pArray[nPos].Name ); + + UINT32 nFormat = 0; + for (long i=0; i<nDataCount; i++) + if (aName == aDataNames[i]) //! search more efficiently? + { + nFormat = nDataFormats[i]; + break; + } + pNumFmt[nPos] = nFormat; + } + } + + rFormats = pNumFmt; + rCount = nSize; + } + } +} + +UINT32 lcl_GetFirstNumberFormat( const uno::Reference<container::XIndexAccess>& xDims ) +{ + long nDimCount = xDims->getCount(); + for (long nDim=0; nDim<nDimCount; nDim++) + { + uno::Reference<uno::XInterface> xDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + if ( xDimProp.is() ) + { + sheet::DataPilotFieldOrientation eDimOrient = + (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA ) + { + long nFormat = ScUnoHelpFunctions::GetLongProperty( + xDimProp, + rtl::OUString::createFromAscii(DP_PROP_NUMBERFORMAT) ); + + return nFormat; // use format from first found data dimension + } + } + } + + return 0; // none found +} + +void lcl_SortFields( ScDPOutLevelData* pFields, long nFieldCount ) +{ + for (long i=0; i+1<nFieldCount; i++) + { + for (long j=0; j+i+1<nFieldCount; j++) + if ( pFields[j+1] < pFields[j] ) + pFields[j].Swap( pFields[j+1] ); + } +} + +BOOL lcl_MemberEmpty( const uno::Sequence<sheet::MemberResult>& rSeq ) +{ + // used to skip levels that have no members + + long nLen = rSeq.getLength(); + const sheet::MemberResult* pArray = rSeq.getConstArray(); + for (long i=0; i<nLen; i++) + if (pArray[i].Flags & sheet::MemberResultFlags::HASMEMBER) + return FALSE; + + return TRUE; // no member data -> empty +} + +uno::Sequence<sheet::MemberResult> lcl_GetSelectedPageAsResult( const uno::Reference<beans::XPropertySet>& xDimProp ) +{ + uno::Sequence<sheet::MemberResult> aRet; + if ( xDimProp.is() ) + { + try + { + //! merge with ScDPDimension::setPropertyValue? + + uno::Any aValue = xDimProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FILTER) ); + + uno::Sequence<sheet::TableFilterField> aSeq; + if (aValue >>= aSeq) + { + if ( aSeq.getLength() == 1 ) + { + const sheet::TableFilterField& rField = aSeq[0]; + if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric ) + { + rtl::OUString aSelectedPage( rField.StringValue ); + //! different name/caption string? + sheet::MemberResult aResult( aSelectedPage, aSelectedPage, 0 ); + aRet = uno::Sequence<sheet::MemberResult>( &aResult, 1 ); + } + } + // else return empty sequence + } + } + catch ( uno::Exception& ) + { + // recent addition - allow source to not handle it (no error) + } + } + return aRet; +} + +ScDPOutput::ScDPOutput( ScDocument* pD, const uno::Reference<sheet::XDimensionsSupplier>& xSrc, + const ScAddress& rPos, BOOL bFilter ) : + pDoc( pD ), + xSource( xSrc ), + aStartPos( rPos ), + bDoFilter( bFilter ), + bResultsError( FALSE ), + pColNumFmt( NULL ), + pRowNumFmt( NULL ), + nColFmtCount( 0 ), + nRowFmtCount( 0 ), + nSingleNumFmt( 0 ), + bSizesValid( FALSE ), + bSizeOverflow( FALSE ) +{ + nTabStartCol = nMemberStartCol = nDataStartCol = nTabEndCol = 0; + nTabStartRow = nMemberStartRow = nDataStartRow = nTabEndRow = 0; + + pColFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS]; + pRowFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS]; + pPageFields = new ScDPOutLevelData[SC_DPOUT_MAXLEVELS]; + nColFieldCount = 0; + nRowFieldCount = 0; + nPageFieldCount = 0; + + uno::Reference<sheet::XDataPilotResults> xResult( xSource, uno::UNO_QUERY ); + if ( xSource.is() && xResult.is() ) + { + // get dimension results: + + uno::Reference<container::XIndexAccess> xDims = + new ScNameToIndexAccess( xSource->getDimensions() ); + long nDimCount = xDims->getCount(); + for (long nDim=0; nDim<nDimCount; nDim++) + { + uno::Reference<uno::XInterface> xDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + uno::Reference<sheet::XHierarchiesSupplier> xDimSupp( xDim, uno::UNO_QUERY ); + if ( xDimProp.is() && xDimSupp.is() ) + { + sheet::DataPilotFieldOrientation eDimOrient = + (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + long nDimPos = ScUnoHelpFunctions::GetLongProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_POSITION) ); + BOOL bIsDataLayout = ScUnoHelpFunctions::GetBoolProperty( + xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + + if ( eDimOrient != sheet::DataPilotFieldOrientation_HIDDEN ) + { + uno::Reference<container::XIndexAccess> xHiers = + new ScNameToIndexAccess( xDimSupp->getHierarchies() ); + long nHierarchy = ScUnoHelpFunctions::GetLongProperty( + xDimProp, + rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY) ); + if ( nHierarchy >= xHiers->getCount() ) + nHierarchy = 0; + + uno::Reference<uno::XInterface> xHier = + ScUnoHelpFunctions::AnyToInterface( + xHiers->getByIndex(nHierarchy) ); + uno::Reference<sheet::XLevelsSupplier> xHierSupp( xHier, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XIndexAccess> xLevels = + new ScNameToIndexAccess( xHierSupp->getLevels() ); + long nLevCount = xLevels->getCount(); + for (long nLev=0; nLev<nLevCount; nLev++) + { + uno::Reference<uno::XInterface> xLevel = + ScUnoHelpFunctions::AnyToInterface( + xLevels->getByIndex(nLev) ); + uno::Reference<container::XNamed> xLevNam( xLevel, uno::UNO_QUERY ); + uno::Reference<sheet::XDataPilotMemberResults> xLevRes( + xLevel, uno::UNO_QUERY ); + if ( xLevNam.is() && xLevRes.is() ) + { + String aCaption = String(xLevNam->getName()); //! Caption... + switch ( eDimOrient ) + { + case sheet::DataPilotFieldOrientation_COLUMN: + pColFields[nColFieldCount].nDim = nDim; + pColFields[nColFieldCount].nHier = nHierarchy; + pColFields[nColFieldCount].nLevel = nLev; + pColFields[nColFieldCount].nDimPos = nDimPos; + pColFields[nColFieldCount].aResult = xLevRes->getResults(); + pColFields[nColFieldCount].aCaption= aCaption; + if (!lcl_MemberEmpty(pColFields[nColFieldCount].aResult)) + ++nColFieldCount; + break; + case sheet::DataPilotFieldOrientation_ROW: + pRowFields[nRowFieldCount].nDim = nDim; + pRowFields[nRowFieldCount].nHier = nHierarchy; + pRowFields[nRowFieldCount].nLevel = nLev; + pRowFields[nRowFieldCount].nDimPos = nDimPos; + pRowFields[nRowFieldCount].aResult = xLevRes->getResults(); + pRowFields[nRowFieldCount].aCaption= aCaption; + if (!lcl_MemberEmpty(pRowFields[nRowFieldCount].aResult)) + ++nRowFieldCount; + break; + case sheet::DataPilotFieldOrientation_PAGE: + pPageFields[nPageFieldCount].nDim = nDim; + pPageFields[nPageFieldCount].nHier = nHierarchy; + pPageFields[nPageFieldCount].nLevel = nLev; + pPageFields[nPageFieldCount].nDimPos = nDimPos; + pPageFields[nPageFieldCount].aResult = lcl_GetSelectedPageAsResult(xDimProp); + pPageFields[nPageFieldCount].aCaption= aCaption; + // no check on results for page fields + ++nPageFieldCount; + break; + default: + { + // added to avoid warnings + } + } + + // get number formats from data dimensions + if ( bIsDataLayout ) + { + DBG_ASSERT( nLevCount == 1, "data layout: multiple levels?" ); + if ( eDimOrient == sheet::DataPilotFieldOrientation_COLUMN ) + lcl_FillNumberFormats( pColNumFmt, nColFmtCount, xLevRes, xDims ); + else if ( eDimOrient == sheet::DataPilotFieldOrientation_ROW ) + lcl_FillNumberFormats( pRowNumFmt, nRowFmtCount, xLevRes, xDims ); + } + } + } + } + } + else if ( bIsDataLayout ) + { + // data layout dimension is hidden (allowed if there is only one data dimension) + // -> use the number format from the first data dimension for all results + + nSingleNumFmt = lcl_GetFirstNumberFormat( xDims ); + } + } + } + lcl_SortFields( pColFields, nColFieldCount ); + lcl_SortFields( pRowFields, nRowFieldCount ); + lcl_SortFields( pPageFields, nPageFieldCount ); + + // get data results: + + try + { + aData = xResult->getResults(); + } + catch (uno::RuntimeException&) + { + bResultsError = TRUE; + } + } + + // get "DataDescription" property (may be missing in external sources) + + uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY ); + if ( xSrcProp.is() ) + { + try + { + uno::Any aAny = xSrcProp->getPropertyValue( + rtl::OUString::createFromAscii(DP_PROP_DATADESCR) ); + rtl::OUString aUStr; + aAny >>= aUStr; + aDataDescription = String( aUStr ); + } + catch(uno::Exception&) + { + } + } +} + +ScDPOutput::~ScDPOutput() +{ + delete[] pColFields; + delete[] pRowFields; + delete[] pPageFields; + + delete[] pColNumFmt; + delete[] pRowNumFmt; +} + +void ScDPOutput::SetPosition( const ScAddress& rPos ) +{ + aStartPos = rPos; + bSizesValid = bSizeOverflow = FALSE; +} + +void ScDPOutput::DataCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const sheet::DataResult& rData ) +{ + long nFlags = rData.Flags; + if ( nFlags & sheet::DataResultFlags::ERROR ) + { + pDoc->SetError( nCol, nRow, nTab, errNoValue ); + } + else if ( nFlags & sheet::DataResultFlags::HASDATA ) + { + pDoc->SetValue( nCol, nRow, nTab, rData.Value ); + + // use number formats from source + + DBG_ASSERT( bSizesValid, "DataCell: !bSizesValid" ); + UINT32 nFormat = 0; + if ( pColNumFmt ) + { + if ( nCol >= nDataStartCol ) + { + long nIndex = nCol - nDataStartCol; + if ( nIndex < nColFmtCount ) + nFormat = pColNumFmt[nIndex]; + } + } + else if ( pRowNumFmt ) + { + if ( nRow >= nDataStartRow ) + { + long nIndex = nRow - nDataStartRow; + if ( nIndex < nRowFmtCount ) + nFormat = pRowNumFmt[nIndex]; + } + } + else if ( nSingleNumFmt != 0 ) + nFormat = nSingleNumFmt; // single format is used everywhere + if ( nFormat != 0 ) + pDoc->ApplyAttr( nCol, nRow, nTab, SfxUInt32Item( ATTR_VALUE_FORMAT, nFormat ) ); + } + else + { + //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING ); + } + + // SubTotal formatting is controlled by headers +} + +void ScDPOutput::HeaderCell( SCCOL nCol, SCROW nRow, SCTAB nTab, + const sheet::MemberResult& rData, BOOL bColHeader, long nLevel ) +{ + long nFlags = rData.Flags; + if ( nFlags & sheet::MemberResultFlags::HASMEMBER ) + { + pDoc->SetString( nCol, nRow, nTab, rData.Caption ); + } + else + { + //pDoc->SetString( nCol, nRow, nTab, EMPTY_STRING ); + } + + if ( nFlags & sheet::MemberResultFlags::SUBTOTAL ) + { +// SvxWeightItem aItem( WEIGHT_BOLD ); // weight is in the style + + //! limit frames to horizontal or vertical? + if (bColHeader) + { + lcl_SetFrame( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nTabEndRow, 20 ); + lcl_SetStyleById( pDoc,nTab, nCol,nMemberStartRow+(SCROW)nLevel, nCol,nDataStartRow-1, + STR_PIVOT_STYLE_TITLE ); + lcl_SetStyleById( pDoc,nTab, nCol,nDataStartRow, nCol,nTabEndRow, + STR_PIVOT_STYLE_RESULT ); + } + else + { + lcl_SetFrame( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nTabEndCol,nRow, 20 ); + lcl_SetStyleById( pDoc,nTab, nMemberStartCol+(SCCOL)nLevel,nRow, nDataStartCol-1,nRow, + STR_PIVOT_STYLE_TITLE ); + lcl_SetStyleById( pDoc,nTab, nDataStartCol,nRow, nTabEndCol,nRow, + STR_PIVOT_STYLE_RESULT ); + } + } +} + +void ScDPOutput::FieldCell( SCCOL nCol, SCROW nRow, SCTAB nTab, const String& rCaption, BOOL bFrame ) +{ + pDoc->SetString( nCol, nRow, nTab, rCaption ); + if (bFrame) + lcl_SetFrame( pDoc,nTab, nCol,nRow, nCol,nRow, 20 ); + + // Button + pDoc->ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr(SC_MF_BUTTON) ); + + lcl_SetStyleById( pDoc,nTab, nCol,nRow, nCol,nRow, STR_PIVOT_STYLE_FIELDNAME ); +} + +void lcl_DoFilterButton( ScDocument* pDoc, SCCOL nCol, SCROW nRow, SCTAB nTab ) +{ + pDoc->SetString( nCol, nRow, nTab, ScGlobal::GetRscString(STR_CELL_FILTER) ); + pDoc->ApplyAttr( nCol, nRow, nTab, ScMergeFlagAttr(SC_MF_BUTTON) ); +} + +void ScDPOutput::CalcSizes() +{ + if (!bSizesValid) + { + // get column size of data from first row + //! allow different sizes (and clear following areas) ??? + + nRowCount = aData.getLength(); + const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray(); + nColCount = nRowCount ? ( pRowAry[0].getLength() ) : 0; + nHeaderSize = 1; // one row for field names + + // calculate output positions and sizes + + long nPageSize = 0; //! use page fields! + if ( bDoFilter || nPageFieldCount ) + { + nPageSize += nPageFieldCount + 1; // plus one empty row + if ( bDoFilter ) + ++nPageSize; // filter button above the page fields + } + + if ( aStartPos.Col() + nRowFieldCount + nColCount - 1 > MAXCOL || + aStartPos.Row() + nPageSize + nHeaderSize + nColFieldCount + nRowCount > MAXROW ) + { + bSizeOverflow = TRUE; + } + + nTabStartCol = aStartPos.Col(); + nTabStartRow = aStartPos.Row() + (SCROW)nPageSize; // below page fields + nMemberStartCol = nTabStartCol; + nMemberStartRow = nTabStartRow + (SCROW) nHeaderSize; + nDataStartCol = nMemberStartCol + (SCCOL)nRowFieldCount; + nDataStartRow = nMemberStartRow + (SCROW)nColFieldCount; + if ( nColCount > 0 ) + nTabEndCol = nDataStartCol + (SCCOL)nColCount - 1; + else + nTabEndCol = nDataStartCol; // single column will remain empty + // if page fields are involved, include the page selection cells + if ( nPageFieldCount > 0 && nTabEndCol < nTabStartCol + 1 ) + nTabEndCol = nTabStartCol + 1; + if ( nRowCount > 0 ) + nTabEndRow = nDataStartRow + (SCROW)nRowCount - 1; + else + nTabEndRow = nDataStartRow; // single row will remain empty + bSizesValid = TRUE; + } +} + +sal_Int32 ScDPOutput::GetPositionType(const ScAddress& rPos) +{ + using namespace ::com::sun::star::sheet; + + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() ) + return DataPilotTablePositionType::NOT_IN_TABLE; + + CalcSizes(); + + // Make sure the cursor is within the table. + if (nCol < nTabStartCol || nRow < nTabStartRow || nCol > nTabEndCol || nRow > nTabEndRow) + return DataPilotTablePositionType::NOT_IN_TABLE; + + // test for result data area. + if (nCol >= nDataStartCol && nCol <= nTabEndCol && nRow >= nDataStartRow && nRow <= nTabEndRow) + return DataPilotTablePositionType::RESULT; + + bool bInColHeader = (nRow >= nTabStartRow && nRow < nDataStartRow); + bool bInRowHeader = (nCol >= nTabStartCol && nCol < nDataStartCol); + + if (bInColHeader && bInRowHeader) + // probably in that ugly little box at the upper-left corner of the table. + return DataPilotTablePositionType::OTHER; + + if (bInColHeader) + { + if (nRow == nTabStartRow) + // first row in the column header area is always used for column + // field buttons. + return DataPilotTablePositionType::OTHER; + + return DataPilotTablePositionType::COLUMN_HEADER; + } + + if (bInRowHeader) + return DataPilotTablePositionType::ROW_HEADER; + + return DataPilotTablePositionType::OTHER; +} + +void ScDPOutput::Output() +{ + long nField; + SCTAB nTab = aStartPos.Tab(); + const uno::Sequence<sheet::DataResult>* pRowAry = aData.getConstArray(); + + // calculate output positions and sizes + + CalcSizes(); + if ( bSizeOverflow || bResultsError ) // does output area exceed sheet limits? + return; // nothing + + // clear whole (new) output area + //! when modifying table, clear old area + //! include IDF_OBJECTS ??? + pDoc->DeleteAreaTab( aStartPos.Col(), aStartPos.Row(), nTabEndCol, nTabEndRow, nTab, IDF_ALL ); + + if ( bDoFilter ) + lcl_DoFilterButton( pDoc, aStartPos.Col(), aStartPos.Row(), nTab ); + + // output page fields: + + for (nField=0; nField<nPageFieldCount; nField++) + { + SCCOL nHdrCol = aStartPos.Col(); + SCROW nHdrRow = aStartPos.Row() + nField + ( bDoFilter ? 1 : 0 ); + // draw without frame for consistency with filter button: + FieldCell( nHdrCol, nHdrRow, nTab, pPageFields[nField].aCaption, FALSE ); + SCCOL nFldCol = nHdrCol + 1; + + String aPageValue; + if ( pPageFields[nField].aResult.getLength() == 1 ) + aPageValue = pPageFields[nField].aResult[0].Caption; + else + aPageValue = String( ScResId( SCSTR_ALL ) ); //! separate string? + + pDoc->SetString( nFldCol, nHdrRow, nTab, aPageValue ); + + lcl_SetFrame( pDoc,nTab, nFldCol,nHdrRow, nFldCol,nHdrRow, 20 ); + pDoc->ApplyAttr( nFldCol, nHdrRow, nTab, ScMergeFlagAttr(SC_MF_AUTO) ); + //! which style? + } + + // data description + // (may get overwritten by first row field) + + String aDesc = aDataDescription; + if ( !aDesc.Len() ) + { + //! use default string ("result") ? + } + pDoc->SetString( nTabStartCol, nTabStartRow, nTab, aDesc ); + + // set STR_PIVOT_STYLE_INNER for whole data area (subtotals are overwritten) + + if ( nDataStartRow > nTabStartRow ) + lcl_SetStyleById( pDoc, nTab, nTabStartCol, nTabStartRow, nTabEndCol, nDataStartRow-1, + STR_PIVOT_STYLE_TOP ); + lcl_SetStyleById( pDoc, nTab, nDataStartCol, nDataStartRow, nTabEndCol, nTabEndRow, + STR_PIVOT_STYLE_INNER ); + + // output column headers: + + for (nField=0; nField<nColFieldCount; nField++) + { + SCCOL nHdrCol = nDataStartCol + (SCCOL)nField; //! check for overflow + FieldCell( nHdrCol, nTabStartRow, nTab, pColFields[nField].aCaption ); + + SCROW nRowPos = nMemberStartRow + (SCROW)nField; //! check for overflow + const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + long nThisColCount = rSequence.getLength(); + DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ??? + for (long nCol=0; nCol<nThisColCount; nCol++) + { + SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow + HeaderCell( nColPos, nRowPos, nTab, pArray[nCol], TRUE, nField ); + if ( ( pArray[nCol].Flags & sheet::MemberResultFlags::HASMEMBER ) && + !( pArray[nCol].Flags & sheet::MemberResultFlags::SUBTOTAL ) ) + { + if ( nField+1 < nColFieldCount ) + { + long nEnd = nCol; + while ( nEnd+1 < nThisColCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) ) + ++nEnd; + SCCOL nEndColPos = nDataStartCol + (SCCOL)nEnd; //! check for overflow + lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nRowPos, 20 ); + lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nEndColPos,nTabEndRow, 20 ); + + lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nEndColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY ); + } + else + lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nColPos,nDataStartRow-1, STR_PIVOT_STYLE_CATEGORY ); + } + } + } + + // output row headers: + + for (nField=0; nField<nRowFieldCount; nField++) + { + SCCOL nHdrCol = nTabStartCol + (SCCOL)nField; //! check for overflow + SCROW nHdrRow = nDataStartRow - 1; + FieldCell( nHdrCol, nHdrRow, nTab, pRowFields[nField].aCaption ); + + SCCOL nColPos = nMemberStartCol + (SCCOL)nField; //! check for overflow + const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + long nThisRowCount = rSequence.getLength(); + DBG_ASSERT( nThisRowCount == nRowCount, "count mismatch" ); //! ??? + for (long nRow=0; nRow<nThisRowCount; nRow++) + { + SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! check for overflow + HeaderCell( nColPos, nRowPos, nTab, pArray[nRow], FALSE, nField ); + if ( ( pArray[nRow].Flags & sheet::MemberResultFlags::HASMEMBER ) && + !( pArray[nRow].Flags & sheet::MemberResultFlags::SUBTOTAL ) ) + { + if ( nField+1 < nRowFieldCount ) + { + long nEnd = nRow; + while ( nEnd+1 < nThisRowCount && ( pArray[nEnd+1].Flags & sheet::MemberResultFlags::CONTINUE ) ) + ++nEnd; + SCROW nEndRowPos = nDataStartRow + (SCROW)nEnd; //! check for overflow + lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nColPos,nEndRowPos, 20 ); + lcl_SetFrame( pDoc,nTab, nColPos,nRowPos, nTabEndCol,nEndRowPos, 20 ); + + lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nEndRowPos, STR_PIVOT_STYLE_CATEGORY ); + } + else + lcl_SetStyleById( pDoc, nTab, nColPos,nRowPos, nDataStartCol-1,nRowPos, STR_PIVOT_STYLE_CATEGORY ); + } + } + } + + // output data results: + + for (long nRow=0; nRow<nRowCount; nRow++) + { + SCROW nRowPos = nDataStartRow + (SCROW)nRow; //! check for overflow + const sheet::DataResult* pColAry = pRowAry[nRow].getConstArray(); + long nThisColCount = pRowAry[nRow].getLength(); + DBG_ASSERT( nThisColCount == nColCount, "count mismatch" ); //! ??? + for (long nCol=0; nCol<nThisColCount; nCol++) + { + SCCOL nColPos = nDataStartCol + (SCCOL)nCol; //! check for overflow + DataCell( nColPos, nRowPos, nTab, pColAry[nCol] ); + } + } + + // frame around the whole table + + lcl_SetFrame( pDoc,nTab, nDataStartCol,nDataStartRow, nTabEndCol,nTabEndRow, 20 ); + if ( nDataStartCol > nMemberStartCol ) + lcl_SetFrame( pDoc,nTab, nMemberStartCol,nDataStartRow, nDataStartCol-1,nTabEndRow, 20 ); + if ( nDataStartRow > nMemberStartRow ) + lcl_SetFrame( pDoc,nTab, nDataStartCol,nMemberStartRow, nTabEndCol,nDataStartRow-1, 20 ); + + lcl_SetFrame( pDoc,nTab, nTabStartCol,nTabStartRow, nTabEndCol,nTabEndRow, 40 ); +} + +ScRange ScDPOutput::GetOutputRange( sal_Int32 nRegionType ) +{ + using namespace ::com::sun::star::sheet; + + CalcSizes(); + +// fprintf(stdout, "ScDPOutput::GetOutputRange: aStartPos = (%ld, %d)\n", aStartPos.Row(), aStartPos.Col());fflush(stdout); +// fprintf(stdout, "ScDPOutput::GetOutputRange: nTabStart (Row = %ld, Col = %ld)\n", nTabStartRow, nTabStartCol);fflush(stdout); +// fprintf(stdout, "ScDPOutput::GetOutputRange: nMemberStart (Row = %ld, Col = %ld)\n", nMemberStartRow, nMemberStartCol);fflush(stdout); +// fprintf(stdout, "ScDPOutput::GetOutputRange: nDataStart (Row = %ld, Col = %ld)\n", nDataStartRow, nDataStartCol);fflush(stdout); +// fprintf(stdout, "ScDPOutput::GetOutputRange: nTabEnd (Row = %ld, Col = %ld)\n", nTabEndRow, nTabStartCol);fflush(stdout); + + SCTAB nTab = aStartPos.Tab(); + switch (nRegionType) + { + case DataPilotOutputRangeType::RESULT: + return ScRange(nDataStartCol, nDataStartRow, nTab, nTabEndCol, nTabEndRow, nTab); + case DataPilotOutputRangeType::TABLE: + return ScRange(aStartPos.Col(), nTabStartRow, nTab, nTabEndCol, nTabEndRow, nTab); + default: + DBG_ASSERT(nRegionType == DataPilotOutputRangeType::WHOLE, "ScDPOutput::GetOutputRange: unknown region type"); + break; + } + return ScRange(aStartPos.Col(), aStartPos.Row(), nTab, nTabEndCol, nTabEndRow, nTab); +} + +BOOL ScDPOutput::HasError() +{ + CalcSizes(); + + return bSizeOverflow || bResultsError; +} + +long ScDPOutput::GetHeaderRows() +{ + return nPageFieldCount + ( bDoFilter ? 1 : 0 ); +} + +void ScDPOutput::GetMemberResultNames( ScStrCollection& rNames, long nDimension ) +{ + // Return the list of all member names in a dimension's MemberResults. + // Only the dimension has to be compared because this is only used with table data, + // where each dimension occurs only once. + + uno::Sequence<sheet::MemberResult> aMemberResults; + bool bFound = false; + long nField; + + // look in column fields + + for (nField=0; nField<nColFieldCount && !bFound; nField++) + if ( pColFields[nField].nDim == nDimension ) + { + aMemberResults = pColFields[nField].aResult; + bFound = true; + } + + // look in row fields + + for (nField=0; nField<nRowFieldCount && !bFound; nField++) + if ( pRowFields[nField].nDim == nDimension ) + { + aMemberResults = pRowFields[nField].aResult; + bFound = true; + } + + // collect the member names + + if ( bFound ) + { + const sheet::MemberResult* pArray = aMemberResults.getConstArray(); + long nResultCount = aMemberResults.getLength(); + + for (long nItem=0; nItem<nResultCount; nItem++) + { + if ( pArray[nItem].Flags & sheet::MemberResultFlags::HASMEMBER ) + { + StrData* pNew = new StrData( pArray[nItem].Name ); + if ( !rNames.Insert( pNew ) ) + delete pNew; + } + } + } +} + + +void ScDPOutput::GetPositionData(const ScAddress& rPos, DataPilotTablePositionData& rPosData) +{ + using namespace ::com::sun::star::sheet; + + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() ) + return; // wrong sheet + + // calculate output positions and sizes + + CalcSizes(); + + rPosData.PositionType = GetPositionType(rPos); + switch (rPosData.PositionType) + { + case DataPilotTablePositionType::RESULT: + { + vector<DataPilotFieldFilter> aFilters; + GetDataResultPositionData(aFilters, rPos); + sal_Int32 nSize = aFilters.size(); + + DataPilotTableResultData aResData; + aResData.FieldFilters.realloc(nSize); + for (sal_Int32 i = 0; i < nSize; ++i) + aResData.FieldFilters[i] = aFilters[i]; + + aResData.DataFieldIndex = 0; + Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY); + if (xPropSet.is()) + { + sal_Int32 nDataFieldCount = 0; + Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount")); + if ((any >>= nDataFieldCount) && nDataFieldCount > 0) + aResData.DataFieldIndex = (nRow - nDataStartRow) % nDataFieldCount; + } + + // Copy appropriate DataResult object from the cached sheet::DataResult table. + if (aData.getLength() > nRow - nDataStartRow && + aData[nRow-nDataStartRow].getLength() > nCol-nDataStartCol) + aResData.Result = aData[nRow-nDataStartRow][nCol-nDataStartCol]; + + rPosData.PositionData = makeAny(aResData); + return; + } + case DataPilotTablePositionType::COLUMN_HEADER: + { + long nField = nRow - nTabStartRow - 1; // 1st line is used for the buttons + if (nField < 0) + break; + + const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nField].aResult; + if (rSequence.getLength() == 0) + break; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + + long nItem = nCol - nDataStartCol; + // get origin of "continue" fields + while (nItem > 0 && ( pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) + --nItem; + + if (nItem < 0) + break; + + DataPilotTableHeaderData aHeaderData; + aHeaderData.MemberName = OUString(pArray[nItem].Name); + aHeaderData.Flags = pArray[nItem].Flags; + aHeaderData.Dimension = static_cast<sal_Int32>(pColFields[nField].nDim); + aHeaderData.Hierarchy = static_cast<sal_Int32>(pColFields[nField].nHier); + aHeaderData.Level = static_cast<sal_Int32>(pColFields[nField].nLevel); + + rPosData.PositionData = makeAny(aHeaderData); + return; + } + case DataPilotTablePositionType::ROW_HEADER: + { + long nField = nCol - nTabStartCol; + if (nField < 0) + break; + + const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nField].aResult; + if (rSequence.getLength() == 0) + break; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + + long nItem = nRow - nDataStartRow; + // get origin of "continue" fields + while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) + --nItem; + + if (nItem < 0) + break; + + DataPilotTableHeaderData aHeaderData; + aHeaderData.MemberName = OUString(pArray[nItem].Name); + aHeaderData.Flags = pArray[nItem].Flags; + aHeaderData.Dimension = static_cast<sal_Int32>(pRowFields[nField].nDim); + aHeaderData.Hierarchy = static_cast<sal_Int32>(pRowFields[nField].nHier); + aHeaderData.Level = static_cast<sal_Int32>(pRowFields[nField].nLevel); + + rPosData.PositionData = makeAny(aHeaderData); + return; + } + } +} + +bool ScDPOutput::GetDataResultPositionData(vector<sheet::DataPilotFieldFilter>& rFilters, const ScAddress& rPos) +{ + // Check to make sure there is at least one data field. + Reference<beans::XPropertySet> xPropSet(xSource, UNO_QUERY); + if (!xPropSet.is()) + return false; + + sal_Int32 nDataFieldCount = 0; + Any any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii("DataFieldCount")); + if (!(any >>= nDataFieldCount) || nDataFieldCount == 0) + // No data field is present in this datapilot table. + return false; + + bool bColGrand = bool(); + any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_COLGRAND)); + if (!(any >>= bColGrand)) + return false; + + bool bRowGrand = bool(); + any = xPropSet->getPropertyValue(rtl::OUString::createFromAscii(SC_UNO_ROWGRAND)); + if (!(any >>= bRowGrand)) + return false; + + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() ) + return false; // wrong sheet + + CalcSizes(); + + // test for data area. + if (nCol < nDataStartCol || nCol > nTabEndCol || nRow < nDataStartRow || nRow > nTabEndRow) + { + // Cell is outside the data field area. + return false; + } + + bool bFilterByCol = !(bColGrand && (nCol == nTabEndCol)); + bool bFilterByRow = !(bRowGrand && (nRow == nTabEndRow)); + + // column fields + for (SCCOL nColField = 0; nColField < nColFieldCount && bFilterByCol; ++nColField) + { + sheet::DataPilotFieldFilter filter; + filter.FieldName = pColFields[nColField].aCaption; + + const uno::Sequence<sheet::MemberResult> rSequence = pColFields[nColField].aResult; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + + DBG_ASSERT(nDataStartCol + rSequence.getLength() - 1 == nTabEndCol, "ScDPOutput::GetDataFieldCellData: error in geometric assumption"); + + long nItem = nCol - nDataStartCol; + // get origin of "continue" fields + while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) + --nItem; + + filter.MatchValue = pArray[nItem].Name; + rFilters.push_back(filter); + } + + // row fields + for (SCROW nRowField = 0; nRowField < nRowFieldCount && bFilterByRow; ++nRowField) + { + sheet::DataPilotFieldFilter filter; + filter.FieldName = pRowFields[nRowField].aCaption; + + const uno::Sequence<sheet::MemberResult> rSequence = pRowFields[nRowField].aResult; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + + DBG_ASSERT(nDataStartRow + rSequence.getLength() - 1 == nTabEndRow, "ScDPOutput::GetDataFieldCellData: error in geometric assumption"); + + long nItem = nRow - nDataStartRow; + // get origin of "continue" fields + while ( nItem > 0 && (pArray[nItem].Flags & sheet::MemberResultFlags::CONTINUE) ) + --nItem; + + filter.MatchValue = pArray[nItem].Name; + rFilters.push_back(filter); + } + + return true; +} + +// +// helper functions for ScDPOutput::GetPivotData +// + +bool lcl_IsNamedDataField( const ScDPGetPivotDataField& rTarget, const String& rSourceName, const String& rGivenName ) +{ + // match one of the names, ignoring case + return ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rSourceName ) || + ScGlobal::GetpTransliteration()->isEqual( rTarget.maFieldName, rGivenName ); +} + +bool lcl_IsNamedCategoryField( const ScDPGetPivotDataField& rFilter, const ScDPOutLevelData& rField ) +{ + //! name from source instead of caption? + return ScGlobal::GetpTransliteration()->isEqual( rFilter.maFieldName, rField.aCaption ); +} + +bool lcl_IsCondition( const sheet::MemberResult& rResultEntry, const ScDPGetPivotDataField& rFilter ) +{ + //! handle numeric conditions? + return ScGlobal::GetpTransliteration()->isEqual( rResultEntry.Name, rFilter.maValStr ); +} + +bool lcl_CheckPageField( const ScDPOutLevelData& rField, + const std::vector< ScDPGetPivotDataField >& rFilters, + std::vector< BOOL >& rFilterUsed ) +{ + for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size(); ++nFilterPos) + { + if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) ) + { + rFilterUsed[nFilterPos] = TRUE; + + // page field result is empty or the selection as single entry (see lcl_GetSelectedPageAsResult) + if ( rField.aResult.getLength() == 1 && + lcl_IsCondition( rField.aResult[0], rFilters[nFilterPos] ) ) + { + return true; // condition matches page selection + } + else + { + return false; // no page selection or different entry + } + } + } + + return true; // valid if the page field doesn't have a filter +} + +uno::Sequence<sheet::GeneralFunction> lcl_GetSubTotals( + const uno::Reference<sheet::XDimensionsSupplier>& xSource, const ScDPOutLevelData& rField ) +{ + uno::Sequence<sheet::GeneralFunction> aSubTotals; + + uno::Reference<sheet::XHierarchiesSupplier> xHierSupp; + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + sal_Int32 nIntCount = xIntDims->getCount(); + if ( rField.nDim < nIntCount ) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( + xIntDims->getByIndex( rField.nDim ) ); + xHierSupp = uno::Reference<sheet::XHierarchiesSupplier>( xIntDim, uno::UNO_QUERY ); + } + DBG_ASSERT( xHierSupp.is(), "dimension not found" ); + + sal_Int32 nHierCount = 0; + uno::Reference<container::XIndexAccess> xHiers; + if ( xHierSupp.is() ) + { + uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies(); + xHiers = new ScNameToIndexAccess( xHiersName ); + nHierCount = xHiers->getCount(); + } + uno::Reference<uno::XInterface> xHier; + if ( rField.nHier < nHierCount ) + xHier = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex( rField.nHier ) ); + DBG_ASSERT( xHier.is(), "hierarchy not found" ); + + sal_Int32 nLevCount = 0; + uno::Reference<container::XIndexAccess> xLevels; + uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHier, uno::UNO_QUERY ); + if ( xLevSupp.is() ) + { + uno::Reference<container::XNameAccess> xLevsName = xLevSupp->getLevels(); + xLevels = new ScNameToIndexAccess( xLevsName ); + nLevCount = xLevels->getCount(); + } + uno::Reference<uno::XInterface> xLevel; + if ( rField.nLevel < nLevCount ) + xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex( rField.nLevel ) ); + DBG_ASSERT( xLevel.is(), "level not found" ); + + uno::Reference<beans::XPropertySet> xLevelProp( xLevel, uno::UNO_QUERY ); + if ( xLevelProp.is() ) + { + try + { + uno::Any aValue = xLevelProp->getPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS) ); + aValue >>= aSubTotals; + } + catch(uno::Exception&) + { + } + } + + return aSubTotals; +} + +void lcl_FilterInclude( std::vector< BOOL >& rResult, std::vector< sal_Int32 >& rSubtotal, + const ScDPOutLevelData& rField, + const std::vector< ScDPGetPivotDataField >& rFilters, + std::vector< BOOL >& rFilterUsed, + bool& rBeforeDataLayout, + sal_Int32 nGrandTotals, sal_Int32 nDataLayoutIndex, + const std::vector<String>& rDataNames, const std::vector<String>& rGivenNames, + const ScDPGetPivotDataField& rTarget, const uno::Reference<sheet::XDimensionsSupplier>& xSource ) +{ + // returns true if a filter was given for the field + + DBG_ASSERT( rFilters.size() == rFilterUsed.size(), "wrong size" ); + + const bool bIsDataLayout = ( rField.nDim == nDataLayoutIndex ); + if (bIsDataLayout) + rBeforeDataLayout = false; + + bool bHasFilter = false; + ScDPGetPivotDataField aFilter; + if ( !bIsDataLayout ) // selection of data field is handled separately + { + for (SCSIZE nFilterPos = 0; nFilterPos < rFilters.size() && !bHasFilter; ++nFilterPos) + { + if ( lcl_IsNamedCategoryField( rFilters[nFilterPos], rField ) ) + { + aFilter = rFilters[nFilterPos]; + rFilterUsed[nFilterPos] = TRUE; + bHasFilter = true; + } + } + } + + bool bHasFunc = bHasFilter && aFilter.meFunction != sheet::GeneralFunction_NONE; + + uno::Sequence<sheet::GeneralFunction> aSubTotals; + if ( !bIsDataLayout ) + aSubTotals = lcl_GetSubTotals( xSource, rField ); + bool bManualSub = ( aSubTotals.getLength() > 0 && aSubTotals[0] != sheet::GeneralFunction_AUTO ); + + const uno::Sequence<sheet::MemberResult>& rSequence = rField.aResult; + const sheet::MemberResult* pArray = rSequence.getConstArray(); + sal_Int32 nSize = rSequence.getLength(); + + DBG_ASSERT( (sal_Int32)rResult.size() == nSize, "Number of fields do not match result count" ); + + sal_Int32 nContCount = 0; + sal_Int32 nSubTotalCount = 0; + sheet::MemberResult aPrevious; + for( sal_Int32 j=0; j < nSize; j++ ) + { + sheet::MemberResult aResultEntry = pArray[j]; + if ( aResultEntry.Flags & sheet::MemberResultFlags::CONTINUE ) + { + aResultEntry = aPrevious; + ++nContCount; + } + else if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) == 0 ) + { + // count the CONTINUE entries before a SUBTOTAL + nContCount = 0; + } + + if ( j >= nSize - nGrandTotals ) + { + // mark as subtotal for the preceding data + if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 ) + { + rSubtotal[j] = nSize - nGrandTotals; + + if ( rResult[j] && nGrandTotals > 1 ) + { + // grand total is always automatic + sal_Int32 nDataPos = j - ( nSize - nGrandTotals ); + DBG_ASSERT( nDataPos < (sal_Int32)rDataNames.size(), "wrong data count" ); + String aSourceName( rDataNames[nDataPos] ); // vector contains source names + String aGivenName( rGivenNames[nDataPos] ); + + rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ); + } + } + + // treat "grand total" columns/rows as empty description, as if they were marked + // in a previous field + + DBG_ASSERT( ( aResultEntry.Flags & + ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == 0 || + ( aResultEntry.Flags & + ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ) ) == + ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL ), + "non-subtotal member found in grand total result" ); + aResultEntry.Flags = 0; + } + + // mark subtotals (not grand total) for preceding data (assume CONTINUE is set) + if ( ( aResultEntry.Flags & sheet::MemberResultFlags::SUBTOTAL ) != 0 ) + { + rSubtotal[j] = nContCount + 1 + nSubTotalCount; + + if ( rResult[j] ) + { + if ( bManualSub ) + { + if ( rBeforeDataLayout ) + { + // manual subtotals and several data fields + + sal_Int32 nDataCount = rDataNames.size(); + sal_Int32 nFuncPos = nSubTotalCount / nDataCount; // outer order: subtotal functions + sal_Int32 nDataPos = nSubTotalCount % nDataCount; // inner order: data fields + + String aSourceName( rDataNames[nDataPos] ); // vector contains source names + String aGivenName( rGivenNames[nDataPos] ); + + DBG_ASSERT( nFuncPos < aSubTotals.getLength(), "wrong subtotal count" ); + rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ) && + aSubTotals[nFuncPos] == aFilter.meFunction; + } + else + { + // manual subtotals for a single data field + + DBG_ASSERT( nSubTotalCount < aSubTotals.getLength(), "wrong subtotal count" ); + rResult[j] = ( aSubTotals[nSubTotalCount] == aFilter.meFunction ); + } + } + else // automatic subtotals + { + if ( rBeforeDataLayout ) + { + DBG_ASSERT( nSubTotalCount < (sal_Int32)rDataNames.size(), "wrong data count" ); + String aSourceName( rDataNames[nSubTotalCount] ); // vector contains source names + String aGivenName( rGivenNames[nSubTotalCount] ); + + rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ); + } + + // if a function was specified, automatic subtotals never match + if ( bHasFunc ) + rResult[j] = FALSE; + } + } + + ++nSubTotalCount; + } + else + nSubTotalCount = 0; + + if( rResult[j] ) + { + if ( bIsDataLayout ) + { + if ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) + { + // Asterisks are added in ScDPSaveData::WriteToSource to create unique names. + //! preserve original name there? + String aSourceName( aResultEntry.Name ); + aSourceName.EraseTrailingChars( '*' ); + + String aGivenName( aResultEntry.Caption ); //! Should use a stored name when available + aGivenName.EraseLeadingChars( '\'' ); + + rResult[j] = lcl_IsNamedDataField( rTarget, aSourceName, aGivenName ); + } + } + else if ( bHasFilter ) + { + // name must match (simple value or subtotal) + rResult[j] = ( ( aResultEntry.Flags & sheet::MemberResultFlags::HASMEMBER ) != 0 ) && + lcl_IsCondition( aResultEntry, aFilter ); + + // if a function was specified, simple (non-subtotal) values never match + if ( bHasFunc && nSubTotalCount == 0 ) + rResult[j] = FALSE; + } + // if no condition is given, keep the columns/rows included + } + aPrevious = aResultEntry; + } +} + +void lcl_StripSubTotals( std::vector< BOOL >& rResult, const std::vector< sal_Int32 >& rSubtotal ) +{ + sal_Int32 nSize = rResult.size(); + DBG_ASSERT( (sal_Int32)rSubtotal.size() == nSize, "sizes don't match" ); + + for (sal_Int32 nPos=0; nPos<nSize; nPos++) + if ( rResult[nPos] && rSubtotal[nPos] ) + { + // if a subtotal is included, clear the result flag for the columns/rows that the subtotal includes + sal_Int32 nStart = nPos - rSubtotal[nPos]; + DBG_ASSERT( nStart >= 0, "invalid subtotal count" ); + + for (sal_Int32 nPrev = nStart; nPrev < nPos; nPrev++) + rResult[nPrev] = FALSE; + } +} + +String lcl_GetDataFieldName( const String& rSourceName, sheet::GeneralFunction eFunc ) +{ + USHORT nStrId = 0; + switch ( eFunc ) + { + case sheet::GeneralFunction_SUM: nStrId = STR_FUN_TEXT_SUM; break; + case sheet::GeneralFunction_COUNT: + case sheet::GeneralFunction_COUNTNUMS: nStrId = STR_FUN_TEXT_COUNT; break; + case sheet::GeneralFunction_AVERAGE: nStrId = STR_FUN_TEXT_AVG; break; + case sheet::GeneralFunction_MAX: nStrId = STR_FUN_TEXT_MAX; break; + case sheet::GeneralFunction_MIN: nStrId = STR_FUN_TEXT_MIN; break; + case sheet::GeneralFunction_PRODUCT: nStrId = STR_FUN_TEXT_PRODUCT; break; + case sheet::GeneralFunction_STDEV: + case sheet::GeneralFunction_STDEVP: nStrId = STR_FUN_TEXT_STDDEV; break; + case sheet::GeneralFunction_VAR: + case sheet::GeneralFunction_VARP: nStrId = STR_FUN_TEXT_VAR; break; + case sheet::GeneralFunction_NONE: + case sheet::GeneralFunction_AUTO: + default: + { + DBG_ERRORFILE("wrong function"); + } + } + if ( !nStrId ) + return String(); + + String aRet( ScGlobal::GetRscString( nStrId ) ); + aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " )); + aRet.Append( rSourceName ); + return aRet; +} + +// static +void ScDPOutput::GetDataDimensionNames( String& rSourceName, String& rGivenName, + const uno::Reference<uno::XInterface>& xDim ) +{ + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + uno::Reference<container::XNamed> xDimName( xDim, uno::UNO_QUERY ); + if ( xDimProp.is() && xDimName.is() ) + { + // Asterisks are added in ScDPSaveData::WriteToSource to create unique names. + //! preserve original name there? + rSourceName = xDimName->getName(); + rSourceName.EraseTrailingChars( '*' ); + + // Generate "given name" the same way as in dptabres. + //! Should use a stored name when available + + sheet::GeneralFunction eFunc = (sheet::GeneralFunction)ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_FUNCTION), + sheet::GeneralFunction_NONE ); + rGivenName = lcl_GetDataFieldName( rSourceName, eFunc ); + } +} + +void lcl_GetTableVars( sal_Int32& rGrandTotalCols, sal_Int32& rGrandTotalRows, sal_Int32& rDataLayoutIndex, + std::vector<String>& rDataNames, std::vector<String>& rGivenNames, + sheet::DataPilotFieldOrientation& rDataOrient, + const uno::Reference<sheet::XDimensionsSupplier>& xSource ) +{ + rDataLayoutIndex = -1; // invalid + rGrandTotalCols = 0; + rGrandTotalRows = 0; + rDataOrient = sheet::DataPilotFieldOrientation_HIDDEN; + + uno::Reference<beans::XPropertySet> xSrcProp( xSource, uno::UNO_QUERY ); + BOOL bColGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp, + rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND) ); + if ( bColGrand ) + rGrandTotalCols = 1; // default if data layout not in columns + + BOOL bRowGrand = ScUnoHelpFunctions::GetBoolProperty( xSrcProp, + rtl::OUString::createFromAscii(DP_PROP_ROWGRAND) ); + if ( bRowGrand ) + rGrandTotalRows = 1; // default if data layout not in rows + + if ( xSource.is() ) + { + // find index and orientation of "data layout" dimension, count data dimensions + + sal_Int32 nDataCount = 0; + + uno::Reference<container::XIndexAccess> xDims = new ScNameToIndexAccess( xSource->getDimensions() ); + long nDimCount = xDims->getCount(); + for (long nDim=0; nDim<nDimCount; nDim++) + { + uno::Reference<uno::XInterface> xDim = + ScUnoHelpFunctions::AnyToInterface( xDims->getByIndex(nDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + if ( xDimProp.is() ) + { + sheet::DataPilotFieldOrientation eDimOrient = + (sheet::DataPilotFieldOrientation) ScUnoHelpFunctions::GetEnumProperty( + xDimProp, rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), + sheet::DataPilotFieldOrientation_HIDDEN ); + if ( ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ) ) + { + rDataLayoutIndex = nDim; + rDataOrient = eDimOrient; + } + if ( eDimOrient == sheet::DataPilotFieldOrientation_DATA ) + { + String aSourceName; + String aGivenName; + ScDPOutput::GetDataDimensionNames( aSourceName, aGivenName, xDim ); + rDataNames.push_back( aSourceName ); + rGivenNames.push_back( aGivenName ); + + ++nDataCount; + } + } + } + + if ( ( rDataOrient == sheet::DataPilotFieldOrientation_COLUMN ) && bColGrand ) + rGrandTotalCols = nDataCount; + else if ( ( rDataOrient == sheet::DataPilotFieldOrientation_ROW ) && bRowGrand ) + rGrandTotalRows = nDataCount; + } +} + +// Returns TRUE on success and stores the result in rTarget +// Returns FALSE if rFilters or rTarget describes something that is not visible +BOOL ScDPOutput::GetPivotData( ScDPGetPivotDataField& rTarget, + const std::vector< ScDPGetPivotDataField >& rFilters ) +{ + CalcSizes(); + + // need to know about grand total columns/rows: + sal_Int32 nGrandTotalCols; + sal_Int32 nGrandTotalRows; + sal_Int32 nDataLayoutIndex; + std::vector<String> aDataNames; + std::vector<String> aGivenNames; + sheet::DataPilotFieldOrientation eDataOrient; + lcl_GetTableVars( nGrandTotalCols, nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, eDataOrient, xSource ); + + if ( aDataNames.empty() ) + return FALSE; // incomplete table without data fields -> no result + + if ( eDataOrient == sheet::DataPilotFieldOrientation_HIDDEN ) + { + // no data layout field -> single data field -> must match the selected field in rTarget + + DBG_ASSERT( aDataNames.size() == 1, "several data fields but no data layout field" ); + if ( !lcl_IsNamedDataField( rTarget, aDataNames[0], aGivenNames[0] ) ) + return FALSE; + } + + std::vector< BOOL > aIncludeCol( nColCount, TRUE ); + std::vector< sal_Int32 > aSubtotalCol( nColCount, 0 ); + std::vector< BOOL > aIncludeRow( nRowCount, TRUE ); + std::vector< sal_Int32 > aSubtotalRow( nRowCount, 0 ); + + std::vector< BOOL > aFilterUsed( rFilters.size(), FALSE ); + + long nField; + long nCol; + long nRow; + bool bBeforeDataLayout; + + // look in column fields + + bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_COLUMN ); + for (nField=0; nField<nColFieldCount; nField++) + lcl_FilterInclude( aIncludeCol, aSubtotalCol, pColFields[nField], rFilters, aFilterUsed, bBeforeDataLayout, + nGrandTotalCols, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource ); + + // look in row fields + + bBeforeDataLayout = ( eDataOrient == sheet::DataPilotFieldOrientation_ROW ); + for (nField=0; nField<nRowFieldCount; nField++) + lcl_FilterInclude( aIncludeRow, aSubtotalRow, pRowFields[nField], rFilters, aFilterUsed, bBeforeDataLayout, + nGrandTotalRows, nDataLayoutIndex, aDataNames, aGivenNames, rTarget, xSource ); + + // page fields + + for (nField=0; nField<nPageFieldCount; nField++) + if ( !lcl_CheckPageField( pPageFields[nField], rFilters, aFilterUsed ) ) + return FALSE; + + // all filter fields must be used + for (SCSIZE nFilter=0; nFilter<aFilterUsed.size(); nFilter++) + if (!aFilterUsed[nFilter]) + return FALSE; + + lcl_StripSubTotals( aIncludeCol, aSubtotalCol ); + lcl_StripSubTotals( aIncludeRow, aSubtotalRow ); + + long nColPos = 0; + long nColIncluded = 0; + for (nCol=0; nCol<nColCount; nCol++) + if (aIncludeCol[nCol]) + { + nColPos = nCol; + ++nColIncluded; + } + + long nRowPos = 0; + long nRowIncluded = 0; + for (nRow=0; nRow<nRowCount; nRow++) + if (aIncludeRow[nRow]) + { + nRowPos = nRow; + ++nRowIncluded; + } + + if ( nColIncluded != 1 || nRowIncluded != 1 ) + return FALSE; + + const uno::Sequence<sheet::DataResult>& rDataRow = aData[nRowPos]; + if ( nColPos >= rDataRow.getLength() ) + return FALSE; + + const sheet::DataResult& rResult = rDataRow[nColPos]; + if ( rResult.Flags & sheet::DataResultFlags::ERROR ) + return FALSE; //! different error? + + rTarget.mbValIsStr = FALSE; + rTarget.mnValNum = rResult.Value; + + return TRUE; +} + +BOOL ScDPOutput::IsFilterButton( const ScAddress& rPos ) +{ + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() || !bDoFilter ) + return FALSE; // wrong sheet or no button at all + + // filter button is at top left + return ( nCol == aStartPos.Col() && nRow == aStartPos.Row() ); +} + +long ScDPOutput::GetHeaderDim( const ScAddress& rPos, USHORT& rOrient ) +{ + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() ) + return -1; // wrong sheet + + // calculate output positions and sizes + + CalcSizes(); + + // test for column header + + if ( nRow == nTabStartRow && nCol >= nDataStartCol && nCol < nDataStartCol + nColFieldCount ) + { + rOrient = sheet::DataPilotFieldOrientation_COLUMN; + long nField = nCol - nDataStartCol; + return pColFields[nField].nDim; + } + + // test for row header + + if ( nRow+1 == nDataStartRow && nCol >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) + { + rOrient = sheet::DataPilotFieldOrientation_ROW; + long nField = nCol - nTabStartCol; + return pRowFields[nField].nDim; + } + + // test for page field + + SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 ); + if ( nCol == aStartPos.Col() && nRow >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount ) + { + rOrient = sheet::DataPilotFieldOrientation_PAGE; + long nField = nRow - nPageStartRow; + return pPageFields[nField].nDim; + } + + //! single data field (?) + + rOrient = sheet::DataPilotFieldOrientation_HIDDEN; + return -1; // invalid +} + +BOOL ScDPOutput::GetHeaderDrag( const ScAddress& rPos, BOOL bMouseLeft, BOOL bMouseTop, + long nDragDim, + Rectangle& rPosRect, USHORT& rOrient, long& rDimPos ) +{ + // Rectangle instead of ScRange for rPosRect to allow for negative values + + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + if ( nTab != aStartPos.Tab() ) + return FALSE; // wrong sheet + + // calculate output positions and sizes + + CalcSizes(); + + // test for column header + + if ( nCol >= nDataStartCol && nCol <= nTabEndCol && + nRow + 1 >= nMemberStartRow && nRow < nMemberStartRow + nColFieldCount ) + { + long nField = nRow - nMemberStartRow; + if (nField < 0) + { + nField = 0; + bMouseTop = TRUE; + } + //! find start of dimension + + rPosRect = Rectangle( nDataStartCol, nMemberStartRow + nField, + nTabEndCol, nMemberStartRow + nField -1 ); + + BOOL bFound = FALSE; // is this within the same orientation? + BOOL bBeforeDrag = FALSE; + BOOL bAfterDrag = FALSE; + for (long nPos=0; nPos<nColFieldCount && !bFound; nPos++) + { + if (pColFields[nPos].nDim == nDragDim) + { + bFound = TRUE; + if ( nField < nPos ) + bBeforeDrag = TRUE; + else if ( nField > nPos ) + bAfterDrag = TRUE; + } + } + + if ( bFound ) + { + if (!bBeforeDrag) + { + ++rPosRect.Bottom(); + if (bAfterDrag) + ++rPosRect.Top(); + } + } + else + { + if ( !bMouseTop ) + { + ++rPosRect.Top(); + ++rPosRect.Bottom(); + ++nField; + } + } + + rOrient = sheet::DataPilotFieldOrientation_COLUMN; + rDimPos = nField; //!... + return TRUE; + } + + // test for row header + + // special case if no row fields + BOOL bSpecial = ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow && + nRowFieldCount == 0 && nCol == nTabStartCol && bMouseLeft ); + + if ( bSpecial || ( nRow+1 >= nDataStartRow && nRow <= nTabEndRow && + nCol + 1 >= nTabStartCol && nCol < nTabStartCol + nRowFieldCount ) ) + { + long nField = nCol - nTabStartCol; + //! find start of dimension + + rPosRect = Rectangle( nTabStartCol + nField, nDataStartRow - 1, + nTabStartCol + nField - 1, nTabEndRow ); + + BOOL bFound = FALSE; // is this within the same orientation? + BOOL bBeforeDrag = FALSE; + BOOL bAfterDrag = FALSE; + for (long nPos=0; nPos<nRowFieldCount && !bFound; nPos++) + { + if (pRowFields[nPos].nDim == nDragDim) + { + bFound = TRUE; + if ( nField < nPos ) + bBeforeDrag = TRUE; + else if ( nField > nPos ) + bAfterDrag = TRUE; + } + } + + if ( bFound ) + { + if (!bBeforeDrag) + { + ++rPosRect.Right(); + if (bAfterDrag) + ++rPosRect.Left(); + } + } + else + { + if ( !bMouseLeft ) + { + ++rPosRect.Left(); + ++rPosRect.Right(); + ++nField; + } + } + + rOrient = sheet::DataPilotFieldOrientation_ROW; + rDimPos = nField; //!... + return TRUE; + } + + // test for page fields + + SCROW nPageStartRow = aStartPos.Row() + ( bDoFilter ? 1 : 0 ); + if ( nCol >= aStartPos.Col() && nCol <= nTabEndCol && + nRow + 1 >= nPageStartRow && nRow < nPageStartRow + nPageFieldCount ) + { + long nField = nRow - nPageStartRow; + if (nField < 0) + { + nField = 0; + bMouseTop = TRUE; + } + //! find start of dimension + + rPosRect = Rectangle( aStartPos.Col(), nPageStartRow + nField, + nTabEndCol, nPageStartRow + nField - 1 ); + + BOOL bFound = FALSE; // is this within the same orientation? + BOOL bBeforeDrag = FALSE; + BOOL bAfterDrag = FALSE; + for (long nPos=0; nPos<nPageFieldCount && !bFound; nPos++) + { + if (pPageFields[nPos].nDim == nDragDim) + { + bFound = TRUE; + if ( nField < nPos ) + bBeforeDrag = TRUE; + else if ( nField > nPos ) + bAfterDrag = TRUE; + } + } + + if ( bFound ) + { + if (!bBeforeDrag) + { + ++rPosRect.Bottom(); + if (bAfterDrag) + ++rPosRect.Top(); + } + } + else + { + if ( !bMouseTop ) + { + ++rPosRect.Top(); + ++rPosRect.Bottom(); + ++nField; + } + } + + rOrient = sheet::DataPilotFieldOrientation_PAGE; + rDimPos = nField; //!... + return TRUE; + } + + return FALSE; +} + + + diff --git a/sc/source/core/data/dpsave.cxx b/sc/source/core/data/dpsave.cxx new file mode 100644 index 000000000000..62798076afb9 --- /dev/null +++ b/sc/source/core/data/dpsave.cxx @@ -0,0 +1,1102 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpsave.cxx,v $ + * $Revision: 1.13.32.3 $ + * + * 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 "dpsave.hxx" +#include "dpdimsave.hxx" +#include "miscuno.hxx" +#include "scerrors.hxx" +#include "unonames.hxx" +#include "global.hxx" + +#include <tools/debug.hxx> + +#include <com/sun/star/sheet/GeneralFunction.hpp> +#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldLayoutInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotFieldReference.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortInfo.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> +#include <com/sun/star/sheet/TableFilterField.hpp> +#include <com/sun/star/sheet/XHierarchiesSupplier.hpp> +#include <com/sun/star/sheet/XLevelsSupplier.hpp> +#include <com/sun/star/sheet/XMembersSupplier.hpp> +#include <com/sun/star/container/XNamed.hpp> +#include <com/sun/star/util/XCloneable.hpp> + +using namespace com::sun::star; + +// ----------------------------------------------------------------------- + +#define SC_DPSAVEMODE_NO 0 +#define SC_DPSAVEMODE_YES 1 +#define SC_DPSAVEMODE_DONTKNOW 2 + +// ----------------------------------------------------------------------- + +//! move to a header file +//! use names from unonames.hxx? +#define DP_PROP_COLUMNGRAND "ColumnGrand" +#define DP_PROP_FUNCTION "Function" +#define DP_PROP_IGNOREEMPTY "IgnoreEmptyRows" +#define DP_PROP_ISDATALAYOUT "IsDataLayoutDimension" +#define DP_PROP_ISVISIBLE "IsVisible" +#define DP_PROP_ORIENTATION "Orientation" +#define DP_PROP_REPEATIFEMPTY "RepeatIfEmpty" +#define DP_PROP_ROWGRAND "RowGrand" +#define DP_PROP_SHOWDETAILS "ShowDetails" +#define DP_PROP_SHOWEMPTY "ShowEmpty" +#define DP_PROP_SUBTOTALS "SubTotals" +#define DP_PROP_USEDHIERARCHY "UsedHierarchy" +#define DP_PROP_FILTER "Filter" +#define DP_PROP_POSITION "Position" + +// ----------------------------------------------------------------------- + +void lcl_SetBoolProperty( const uno::Reference<beans::XPropertySet>& xProp, + const rtl::OUString& rName, sal_Bool bValue ) +{ + //! move to ScUnoHelpFunctions? + + xProp->setPropertyValue( rName, uno::Any( &bValue, getBooleanCppuType() ) ); +} + +// ----------------------------------------------------------------------- + +ScDPSaveMember::ScDPSaveMember(const String& rName) : + aName( rName ), + nVisibleMode( SC_DPSAVEMODE_DONTKNOW ), + nShowDetailsMode( SC_DPSAVEMODE_DONTKNOW ) +{ +} + +ScDPSaveMember::ScDPSaveMember(const ScDPSaveMember& r) : + aName( r.aName ), + nVisibleMode( r.nVisibleMode ), + nShowDetailsMode( r.nShowDetailsMode ) +{ +} + +ScDPSaveMember::~ScDPSaveMember() +{ +} + +BOOL ScDPSaveMember::operator== ( const ScDPSaveMember& r ) const +{ + if ( aName != r.aName || + nVisibleMode != r.nVisibleMode || + nShowDetailsMode != r.nShowDetailsMode ) + return FALSE; + + return TRUE; +} + +BOOL ScDPSaveMember::HasIsVisible() const +{ + return nVisibleMode != SC_DPSAVEMODE_DONTKNOW; +} + +void ScDPSaveMember::SetIsVisible(BOOL bSet) +{ + nVisibleMode = bSet; +} + +BOOL ScDPSaveMember::HasShowDetails() const +{ + return nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW; +} + +void ScDPSaveMember::SetShowDetails(BOOL bSet) +{ + nShowDetailsMode = bSet; +} + +void ScDPSaveMember::SetName( const String& rNew ) +{ + // Used only if the source member was renamed (groups). + // For UI renaming of members, a layout name must be used. + + aName = rNew; +} + +void ScDPSaveMember::WriteToSource( const uno::Reference<uno::XInterface>& xMember, sal_Int32 nPosition ) +{ + // nothing to do? + if ( nVisibleMode == SC_DPSAVEMODE_DONTKNOW && nShowDetailsMode == SC_DPSAVEMODE_DONTKNOW && nPosition < 0 ) + return; + + uno::Reference<beans::XPropertySet> xMembProp( xMember, uno::UNO_QUERY ); + DBG_ASSERT( xMembProp.is(), "no properties at member" ); + if ( xMembProp.is() ) + { + // exceptions are caught at ScDPSaveData::WriteToSource + + if ( nVisibleMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xMembProp, + rtl::OUString::createFromAscii(DP_PROP_ISVISIBLE), (BOOL)nVisibleMode ); + + if ( nShowDetailsMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xMembProp, + rtl::OUString::createFromAscii(DP_PROP_SHOWDETAILS), (BOOL)nShowDetailsMode ); + + if ( nPosition >= 0 ) + { + try + { + xMembProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_POSITION), uno::Any(nPosition) ); + } + catch ( uno::Exception& ) + { + // position is optional - exception must be ignored + } + } + } +} + +// ----------------------------------------------------------------------- + +ScDPSaveDimension::ScDPSaveDimension(const String& rName, BOOL bDataLayout) : + aName( rName ), + pLayoutName( NULL ), + pSelectedPage( NULL ), + bIsDataLayout( bDataLayout ), + bDupFlag( FALSE ), + nOrientation( sheet::DataPilotFieldOrientation_HIDDEN ), + nFunction( sheet::GeneralFunction_AUTO ), + nUsedHierarchy( -1 ), + nShowEmptyMode( SC_DPSAVEMODE_DONTKNOW ), + bSubTotalDefault( TRUE ), + nSubTotalCount( 0 ), + pSubTotalFuncs( NULL ), + pReferenceValue( NULL ), + pSortInfo( NULL ), + pAutoShowInfo( NULL ), + pLayoutInfo( NULL ) +{ +} + +ScDPSaveDimension::ScDPSaveDimension(const ScDPSaveDimension& r) : + aName( r.aName ), + bIsDataLayout( r.bIsDataLayout ), + bDupFlag( r.bDupFlag ), + nOrientation( r.nOrientation ), + nFunction( r.nFunction ), + nUsedHierarchy( r.nUsedHierarchy ), + nShowEmptyMode( r.nShowEmptyMode ), + bSubTotalDefault( r.bSubTotalDefault ), + nSubTotalCount( r.nSubTotalCount ), + pSubTotalFuncs( NULL ) +{ + if ( nSubTotalCount && r.pSubTotalFuncs ) + { + pSubTotalFuncs = new USHORT[nSubTotalCount]; + for (long nSub=0; nSub<nSubTotalCount; nSub++) + pSubTotalFuncs[nSub] = r.pSubTotalFuncs[nSub]; + } + + for (MemberList::const_iterator i=r.maMemberList.begin(); i != r.maMemberList.end() ; i++) + { + const String& rName = (*i)->GetName(); + ScDPSaveMember* pNew = new ScDPSaveMember( **i ); + maMemberHash[rName] = pNew; + maMemberList.push_back( pNew ); + } + if (r.pReferenceValue) + pReferenceValue = new sheet::DataPilotFieldReference( *(r.pReferenceValue) ); + else + pReferenceValue = NULL; + if (r.pSortInfo) + pSortInfo = new sheet::DataPilotFieldSortInfo( *(r.pSortInfo) ); + else + pSortInfo = NULL; + if (r.pAutoShowInfo) + pAutoShowInfo = new sheet::DataPilotFieldAutoShowInfo( *(r.pAutoShowInfo) ); + else + pAutoShowInfo = NULL; + if (r.pLayoutInfo) + pLayoutInfo = new sheet::DataPilotFieldLayoutInfo( *(r.pLayoutInfo) ); + else + pLayoutInfo = NULL; + if (r.pLayoutName) + pLayoutName = new String( *(r.pLayoutName) ); + else + pLayoutName = NULL; + if (r.pSelectedPage) + pSelectedPage = new String( *(r.pSelectedPage) ); + else + pSelectedPage = NULL; +} + +ScDPSaveDimension::~ScDPSaveDimension() +{ + for (MemberHash::const_iterator i=maMemberHash.begin(); i != maMemberHash.end() ; i++) + delete i->second; + delete pReferenceValue; + delete pSortInfo; + delete pAutoShowInfo; + delete pLayoutInfo; + delete pLayoutName; + delete pSelectedPage; + delete [] pSubTotalFuncs; +} + +BOOL ScDPSaveDimension::operator== ( const ScDPSaveDimension& r ) const +{ + if ( aName != r.aName || + bIsDataLayout != r.bIsDataLayout || + bDupFlag != r.bDupFlag || + nOrientation != r.nOrientation || + nFunction != r.nFunction || + nUsedHierarchy != r.nUsedHierarchy || + nShowEmptyMode != r.nShowEmptyMode || + bSubTotalDefault != r.bSubTotalDefault || + nSubTotalCount != r.nSubTotalCount ) + return FALSE; + + if ( nSubTotalCount && ( !pSubTotalFuncs || !r.pSubTotalFuncs ) ) // should not happen + return FALSE; + + long i; + for (i=0; i<nSubTotalCount; i++) + if ( pSubTotalFuncs[i] != r.pSubTotalFuncs[i] ) + return FALSE; + + if (maMemberHash.size() != r.maMemberHash.size() ) + return FALSE; + + MemberList::const_iterator a=maMemberList.begin(); + MemberList::const_iterator b=r.maMemberList.begin(); + for (; a != maMemberList.end() ; ++a, ++b) + if (!(**a == **b)) + return FALSE; + + return TRUE; +} + +void ScDPSaveDimension::AddMember(ScDPSaveMember* pMember) +{ + const String & rName = pMember->GetName(); + MemberHash::iterator aExisting = maMemberHash.find( rName ); + if ( aExisting == maMemberHash.end() ) + { + std::pair< const String, ScDPSaveMember *> key( rName, pMember ); + maMemberHash.insert ( key ); + } + else + { + maMemberList.remove( aExisting->second ); + delete aExisting->second; + aExisting->second = pMember; + } + maMemberList.push_back( pMember ); +} + +void ScDPSaveDimension::SetName( const String& rNew ) +{ + // Used only if the source dim was renamed (groups). + // For UI renaming of dimensions, the layout name must be used. + + aName = rNew; +} + +void ScDPSaveDimension::SetOrientation(USHORT nNew) +{ + nOrientation = nNew; +} + +void ScDPSaveDimension::SetSubTotals(long nCount, const USHORT* pFuncs) +{ + if (pSubTotalFuncs) + delete [] pSubTotalFuncs; + nSubTotalCount = nCount; + if ( nCount && pFuncs ) + { + pSubTotalFuncs = new USHORT[nCount]; + for (long i=0; i<nCount; i++) + pSubTotalFuncs[i] = pFuncs[i]; + } + else + pSubTotalFuncs = NULL; + + bSubTotalDefault = FALSE; +} + +void ScDPSaveDimension::SetShowEmpty(BOOL bSet) +{ + nShowEmptyMode = bSet; +} + +void ScDPSaveDimension::SetFunction(USHORT nNew) +{ + nFunction = nNew; +} + +void ScDPSaveDimension::SetUsedHierarchy(long nNew) +{ + nUsedHierarchy = nNew; +} + +BOOL ScDPSaveDimension::HasLayoutName() const +{ + return ( pLayoutName != NULL ); +} + +void ScDPSaveDimension::SetLayoutName(const String* pName) +{ + delete pLayoutName; + if (pName) + pLayoutName = new String( *pName ); + else + pLayoutName = NULL; +} + +const String& ScDPSaveDimension::GetLayoutName() const +{ + if (pLayoutName) + return *pLayoutName; + return aName; +} + +void ScDPSaveDimension::SetReferenceValue(const sheet::DataPilotFieldReference* pNew) +{ + delete pReferenceValue; + if (pNew) + pReferenceValue = new sheet::DataPilotFieldReference(*pNew); + else + pReferenceValue = NULL; +} + +void ScDPSaveDimension::SetSortInfo(const sheet::DataPilotFieldSortInfo* pNew) +{ + delete pSortInfo; + if (pNew) + pSortInfo = new sheet::DataPilotFieldSortInfo(*pNew); + else + pSortInfo = NULL; +} + +void ScDPSaveDimension::SetAutoShowInfo(const sheet::DataPilotFieldAutoShowInfo* pNew) +{ + delete pAutoShowInfo; + if (pNew) + pAutoShowInfo = new sheet::DataPilotFieldAutoShowInfo(*pNew); + else + pAutoShowInfo = NULL; +} + +void ScDPSaveDimension::SetLayoutInfo(const sheet::DataPilotFieldLayoutInfo* pNew) +{ + delete pLayoutInfo; + if (pNew) + pLayoutInfo = new sheet::DataPilotFieldLayoutInfo(*pNew); + else + pLayoutInfo = NULL; +} + +void ScDPSaveDimension::SetCurrentPage( const String* pPage ) +{ + delete pSelectedPage; + if (pPage) + pSelectedPage = new String( *pPage ); + else + pSelectedPage = NULL; +} + +BOOL ScDPSaveDimension::HasCurrentPage() const +{ + return ( pSelectedPage != NULL ); +} + +const String& ScDPSaveDimension::GetCurrentPage() const +{ + if (pSelectedPage) + return *pSelectedPage; + return EMPTY_STRING; +} + +ScDPSaveMember* ScDPSaveDimension::GetExistingMemberByName(const String& rName) +{ + MemberHash::const_iterator res = maMemberHash.find (rName); + if (res != maMemberHash.end()) + return res->second; + return NULL; +} + + +ScDPSaveMember* ScDPSaveDimension::GetMemberByName(const String& rName) +{ + MemberHash::const_iterator res = maMemberHash.find (rName); + if (res != maMemberHash.end()) + return res->second; + + ScDPSaveMember* pNew = new ScDPSaveMember( rName ); + maMemberHash[rName] = pNew; + maMemberList.push_back( pNew ); + return pNew; +} + +void ScDPSaveDimension::SetMemberPosition( const String& rName, sal_Int32 nNewPos ) +{ + ScDPSaveMember* pMember = GetMemberByName( rName ); // make sure it exists and is in the hash + + maMemberList.remove( pMember ); + + MemberList::iterator aIter = maMemberList.begin(); + for (sal_Int32 i=0; i<nNewPos && aIter != maMemberList.end(); i++) + ++aIter; + maMemberList.insert( aIter, pMember ); +} + +void ScDPSaveDimension::WriteToSource( const uno::Reference<uno::XInterface>& xDim ) +{ + uno::Reference<beans::XPropertySet> xDimProp( xDim, uno::UNO_QUERY ); + DBG_ASSERT( xDimProp.is(), "no properties at dimension" ); + if ( xDimProp.is() ) + { + // exceptions are caught at ScDPSaveData::WriteToSource + uno::Any aAny; + + sheet::DataPilotFieldOrientation eOrient = (sheet::DataPilotFieldOrientation)nOrientation; + aAny <<= eOrient; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), aAny ); + + sheet::GeneralFunction eFunc = (sheet::GeneralFunction)nFunction; + aAny <<= eFunc; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FUNCTION), aAny ); + + if ( nUsedHierarchy >= 0 ) + { + aAny <<= (INT32)nUsedHierarchy; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_USEDHIERARCHY), aAny ); + } + + if ( pReferenceValue ) + { + aAny <<= *pReferenceValue; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(SC_UNO_REFVALUE), aAny ); + } + + uno::Sequence<sheet::TableFilterField> aFilter; + // set the selected page field only if the dimension is used as page dimension + if ( pSelectedPage && nOrientation == sheet::DataPilotFieldOrientation_PAGE ) + { + // single filter field: first field equal to selected string + sheet::TableFilterField aField( sheet::FilterConnection_AND, 0, + sheet::FilterOperator_EQUAL, sal_False, 0.0, *pSelectedPage ); + aFilter = uno::Sequence<sheet::TableFilterField>( &aField, 1 ); + } + // else keep empty sequence + try + { + aAny <<= aFilter; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_FILTER), aAny ); + } + catch ( beans::UnknownPropertyException& ) + { + // recent addition - allow source to not handle it (no error) + } + } + + // Level loop outside of maMemberList loop + // because SubTotals have to be set independently of known members + + long nCount = maMemberHash.size(); + + long nHierCount = 0; + uno::Reference<container::XIndexAccess> xHiers; + uno::Reference<sheet::XHierarchiesSupplier> xHierSupp( xDim, uno::UNO_QUERY ); + if ( xHierSupp.is() ) + { + uno::Reference<container::XNameAccess> xHiersName = xHierSupp->getHierarchies(); + xHiers = new ScNameToIndexAccess( xHiersName ); + nHierCount = xHiers->getCount(); + } + + for (long nHier=0; nHier<nHierCount; nHier++) + { + uno::Reference<uno::XInterface> xHierarchy = ScUnoHelpFunctions::AnyToInterface( xHiers->getByIndex(nHier) ); + + long nLevCount = 0; + uno::Reference<container::XIndexAccess> xLevels; + uno::Reference<sheet::XLevelsSupplier> xLevSupp( xHierarchy, uno::UNO_QUERY ); + if ( xLevSupp.is() ) + { + uno::Reference<container::XNameAccess> xLevelsName = xLevSupp->getLevels(); + xLevels = new ScNameToIndexAccess( xLevelsName ); + nLevCount = xLevels->getCount(); + } + + for (long nLev=0; nLev<nLevCount; nLev++) + { + uno::Reference<uno::XInterface> xLevel = ScUnoHelpFunctions::AnyToInterface( xLevels->getByIndex(nLev) ); + uno::Reference<beans::XPropertySet> xLevProp( xLevel, uno::UNO_QUERY ); + DBG_ASSERT( xLevProp.is(), "no properties at level" ); + if ( xLevProp.is() ) + { + uno::Any aAny; + if ( !bSubTotalDefault ) + { + if ( !pSubTotalFuncs ) + nSubTotalCount = 0; + + uno::Sequence<sheet::GeneralFunction> aSeq(nSubTotalCount); + sheet::GeneralFunction* pArray = aSeq.getArray(); + for (long i=0; i<nSubTotalCount; i++) + pArray[i] = (sheet::GeneralFunction)pSubTotalFuncs[i]; + aAny <<= aSeq; + xLevProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_SUBTOTALS), aAny ); + } + if ( nShowEmptyMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xLevProp, + rtl::OUString::createFromAscii(DP_PROP_SHOWEMPTY), (BOOL)nShowEmptyMode ); + + if ( pSortInfo ) + { + aAny <<= *pSortInfo; + try + { + xLevProp->setPropertyValue( rtl::OUString::createFromAscii(SC_UNO_SORTING), aAny ); + } + catch ( beans::UnknownPropertyException& ) + { + // recent addition - allow source to not handle it (no error) + } + } + if ( pAutoShowInfo ) + { + aAny <<= *pAutoShowInfo; + try + { + xLevProp->setPropertyValue( rtl::OUString::createFromAscii(SC_UNO_AUTOSHOW), aAny ); + } + catch ( beans::UnknownPropertyException& ) + { + // recent addition - allow source to not handle it (no error) + } + } + if ( pLayoutInfo ) + { + aAny <<= *pLayoutInfo; + try + { + xLevProp->setPropertyValue( rtl::OUString::createFromAscii(SC_UNO_LAYOUT), aAny ); + } + catch ( beans::UnknownPropertyException& ) + { + // recent addition - allow source to not handle it (no error) + } + } + + // exceptions are caught at ScDPSaveData::WriteToSource + } + + if ( nCount > 0 ) + { + uno::Reference<sheet::XMembersSupplier> xMembSupp( xLevel, uno::UNO_QUERY ); + if ( xMembSupp.is() ) + { + uno::Reference<container::XNameAccess> xMembers = xMembSupp->getMembers(); + if ( xMembers.is() ) + { + sal_Int32 nPosition = -1; // set position only in manual mode + if ( !pSortInfo || pSortInfo->Mode == sheet::DataPilotFieldSortMode::MANUAL ) + nPosition = 0; + + for (MemberList::const_iterator i=maMemberList.begin(); i != maMemberList.end() ; i++) + { + rtl::OUString aMemberName = (*i)->GetName(); + if ( xMembers->hasByName( aMemberName ) ) + { + uno::Reference<uno::XInterface> xMemberInt = ScUnoHelpFunctions::AnyToInterface( + xMembers->getByName( aMemberName ) ); + (*i)->WriteToSource( xMemberInt, nPosition ); + + if ( nPosition >= 0 ) + ++nPosition; // increase if initialized + } + // missing member is no error + } + } + } + } + } + } +} + +// ----------------------------------------------------------------------- + +ScDPSaveData::ScDPSaveData() : + pDimensionData( NULL ), + nColumnGrandMode( SC_DPSAVEMODE_DONTKNOW ), + nRowGrandMode( SC_DPSAVEMODE_DONTKNOW ), + nIgnoreEmptyMode( SC_DPSAVEMODE_DONTKNOW ), + nRepeatEmptyMode( SC_DPSAVEMODE_DONTKNOW ), + bFilterButton( TRUE ), + bDrillDown( TRUE ) +{ +} + +ScDPSaveData::ScDPSaveData(const ScDPSaveData& r) : + nColumnGrandMode( r.nColumnGrandMode ), + nRowGrandMode( r.nRowGrandMode ), + nIgnoreEmptyMode( r.nIgnoreEmptyMode ), + nRepeatEmptyMode( r.nRepeatEmptyMode ), + bFilterButton( r.bFilterButton ), + bDrillDown( r.bDrillDown ) +{ + if ( r.pDimensionData ) + pDimensionData = new ScDPDimensionSaveData( *r.pDimensionData ); + else + pDimensionData = NULL; + + long nCount = r.aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pNew = new ScDPSaveDimension( *(ScDPSaveDimension*)r.aDimList.GetObject(i) ); + aDimList.Insert( pNew, LIST_APPEND ); + } +} + +ScDPSaveData& ScDPSaveData::operator= ( const ScDPSaveData& r ) +{ + if ( &r != this ) + { + delete pDimensionData; + if ( r.pDimensionData ) + pDimensionData = new ScDPDimensionSaveData( *r.pDimensionData ); + else + pDimensionData = NULL; + + nColumnGrandMode = r.nColumnGrandMode; + nRowGrandMode = r.nRowGrandMode; + nIgnoreEmptyMode = r.nIgnoreEmptyMode; + nRepeatEmptyMode = r.nRepeatEmptyMode; + bFilterButton = r.bFilterButton; + bDrillDown = r.bDrillDown; + + // remove old dimensions + + long nCount = aDimList.Count(); + long i; + for (i=0; i<nCount; i++) + delete (ScDPSaveDimension*)aDimList.GetObject(i); + aDimList.Clear(); + + // copy new dimensions + + nCount = r.aDimList.Count(); + for (i=0; i<nCount; i++) + { + ScDPSaveDimension* pNew = + new ScDPSaveDimension( *(ScDPSaveDimension*)r.aDimList.GetObject(i) ); + aDimList.Insert( pNew, LIST_APPEND ); + } + } + return *this; +} + +BOOL ScDPSaveData::operator== ( const ScDPSaveData& r ) const +{ + if ( nColumnGrandMode != r.nColumnGrandMode || + nRowGrandMode != r.nRowGrandMode || + nIgnoreEmptyMode != r.nIgnoreEmptyMode || + nRepeatEmptyMode != r.nRepeatEmptyMode || + bFilterButton != r.bFilterButton || + bDrillDown != r.bDrillDown ) + return FALSE; + + if ( pDimensionData || r.pDimensionData ) + if ( !pDimensionData || !r.pDimensionData || !( *pDimensionData == *r.pDimensionData ) ) + return FALSE; + + ULONG nCount = aDimList.Count(); + if ( nCount != r.aDimList.Count() ) + return FALSE; + + for (ULONG i=0; i<nCount; i++) + if ( !( *(ScDPSaveDimension*)aDimList.GetObject(i) == + *(ScDPSaveDimension*)r.aDimList.GetObject(i) ) ) + return FALSE; + + return TRUE; +} + +ScDPSaveData::~ScDPSaveData() +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + delete (ScDPSaveDimension*)aDimList.GetObject(i); + aDimList.Clear(); + + delete pDimensionData; +} + +ScDPSaveDimension* ScDPSaveData::GetDimensionByName(const String& rName) +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->GetName() == rName && !pDim->IsDataLayout() ) + return pDim; + } + ScDPSaveDimension* pNew = new ScDPSaveDimension( rName, FALSE ); + aDimList.Insert( pNew, LIST_APPEND ); + return pNew; +} + +ScDPSaveDimension* ScDPSaveData::GetExistingDimensionByName(const String& rName) +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->GetName() == rName && !pDim->IsDataLayout() ) + return pDim; + } + return NULL; // don't create new +} + +ScDPSaveDimension* ScDPSaveData::GetNewDimensionByName(const String& rName) +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->GetName() == rName && !pDim->IsDataLayout() ) + return DuplicateDimension(rName); + } + ScDPSaveDimension* pNew = new ScDPSaveDimension( rName, FALSE ); + aDimList.Insert( pNew, LIST_APPEND ); + return pNew; +} + +ScDPSaveDimension* ScDPSaveData::GetDataLayoutDimension() +{ + ULONG nCount = aDimList.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->IsDataLayout() ) + return pDim; + } + ScDPSaveDimension* pNew = new ScDPSaveDimension( String(), TRUE ); + aDimList.Insert( pNew, LIST_APPEND ); + return pNew; +} + +ScDPSaveDimension* ScDPSaveData::DuplicateDimension(const String& rName) +{ + // always insert new + //! check if dimension is there? + + ScDPSaveDimension* pOld = GetDimensionByName( rName ); + ScDPSaveDimension* pNew = new ScDPSaveDimension( *pOld ); + pNew->SetDupFlag( TRUE ); + aDimList.Insert( pNew, LIST_APPEND ); + return pNew; +} + +void ScDPSaveData::RemoveDimensionByName(const String& rName) +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->GetName() == rName && !pDim->IsDataLayout() ) + { + delete pDim; + aDimList.Remove(i); + break; + } + } +} + +ScDPSaveDimension& ScDPSaveData::DuplicateDimension( const ScDPSaveDimension& rDim ) +{ + ScDPSaveDimension* pNew = new ScDPSaveDimension( rDim ); + pNew->SetDupFlag( TRUE ); + aDimList.Insert( pNew, LIST_APPEND ); + return *pNew; +} + +ScDPSaveDimension* ScDPSaveData::GetInnermostDimension(USHORT nOrientation) +{ + // return the innermost dimension for the given orientation, + // excluding data layout dimension + + ScDPSaveDimension* pInner = NULL; + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = static_cast<ScDPSaveDimension*>(aDimList.GetObject(i)); + if ( pDim->GetOrientation() == nOrientation && !pDim->IsDataLayout() ) + pInner = pDim; + } + return pInner; // the last matching one +} + +long ScDPSaveData::GetDataDimensionCount() const +{ + long nDataCount = 0; + + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + const ScDPSaveDimension* pDim = static_cast<const ScDPSaveDimension*>(aDimList.GetObject(i)); + if ( pDim->GetOrientation() == sheet::DataPilotFieldOrientation_DATA ) + ++nDataCount; + } + + return nDataCount; +} + +void ScDPSaveData::SetPosition( ScDPSaveDimension* pDim, long nNew ) +{ + // position (nNew) is counted within dimensions of the same orientation + + USHORT nOrient = pDim->GetOrientation(); + + aDimList.Remove( pDim ); + ULONG nCount = aDimList.Count(); // after remove + + ULONG nInsPos = 0; + while ( nNew > 0 && nInsPos < nCount ) + { + if ( ((ScDPSaveDimension*)aDimList.GetObject(nInsPos))->GetOrientation() == nOrient ) + --nNew; + ++nInsPos; + } + + aDimList.Insert( pDim, nInsPos ); +} + +void ScDPSaveData::SetColumnGrand(BOOL bSet) +{ + nColumnGrandMode = bSet; +} + +void ScDPSaveData::SetRowGrand(BOOL bSet) +{ + nRowGrandMode = bSet; +} + +void ScDPSaveData::SetIgnoreEmptyRows(BOOL bSet) +{ + nIgnoreEmptyMode = bSet; +} + +void ScDPSaveData::SetRepeatIfEmpty(BOOL bSet) +{ + nRepeatEmptyMode = bSet; +} + +void ScDPSaveData::SetFilterButton(BOOL bSet) +{ + bFilterButton = bSet; +} + +void ScDPSaveData::SetDrillDown(BOOL bSet) +{ + bDrillDown = bSet; +} + +void lcl_ResetOrient( const uno::Reference<sheet::XDimensionsSupplier>& xSource ) +{ + sheet::DataPilotFieldOrientation eOrient = sheet::DataPilotFieldOrientation_HIDDEN; + + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + long nIntCount = xIntDims->getCount(); + for (long nIntDim=0; nIntDim<nIntCount; nIntDim++) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nIntDim) ); + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + if (xDimProp.is()) + { + uno::Any aAny; + aAny <<= eOrient; + xDimProp->setPropertyValue( rtl::OUString::createFromAscii(DP_PROP_ORIENTATION), aAny ); + } + } +} + +void ScDPSaveData::WriteToSource( const uno::Reference<sheet::XDimensionsSupplier>& xSource ) +{ + if (!xSource.is()) + return; + + // source options must be first! + + uno::Reference<beans::XPropertySet> xSourceProp( xSource, uno::UNO_QUERY ); + DBG_ASSERT( xSourceProp.is(), "no properties at source" ); + if ( xSourceProp.is() ) + { + // source options are not available for external sources + //! use XPropertySetInfo to test for availability? + + try + { + if ( nIgnoreEmptyMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xSourceProp, + rtl::OUString::createFromAscii(DP_PROP_IGNOREEMPTY), (BOOL)nIgnoreEmptyMode ); + if ( nRepeatEmptyMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xSourceProp, + rtl::OUString::createFromAscii(DP_PROP_REPEATIFEMPTY), (BOOL)nRepeatEmptyMode ); + } + catch(uno::Exception&) + { + // no error + } + } + + // exceptions in the other calls are errors + try + { + // reset all orientations + //! "forgetSettings" or similar at source ????? + //! reset all duplicated dimensions, or reuse them below !!! + + lcl_ResetOrient( xSource ); + + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + rtl::OUString aName = pDim->GetName(); + BOOL bData = pDim->IsDataLayout(); + + //! getByName for ScDPSource, including DataLayoutDimension !!!!!!!! + + uno::Reference<container::XNameAccess> xDimsName = xSource->getDimensions(); + uno::Reference<container::XIndexAccess> xIntDims = new ScNameToIndexAccess( xDimsName ); + long nIntCount = xIntDims->getCount(); + BOOL bFound = FALSE; + for (long nIntDim=0; nIntDim<nIntCount && !bFound; nIntDim++) + { + uno::Reference<uno::XInterface> xIntDim = ScUnoHelpFunctions::AnyToInterface( xIntDims->getByIndex(nIntDim) ); + if ( bData ) + { + uno::Reference<beans::XPropertySet> xDimProp( xIntDim, uno::UNO_QUERY ); + if ( xDimProp.is() ) + { + bFound = ScUnoHelpFunctions::GetBoolProperty( xDimProp, + rtl::OUString::createFromAscii(DP_PROP_ISDATALAYOUT) ); + //! error checking -- is "IsDataLayoutDimension" property required?? + } + } + else + { + uno::Reference<container::XNamed> xDimName( xIntDim, uno::UNO_QUERY ); + if ( xDimName.is() && xDimName->getName() == aName ) + bFound = TRUE; + } + + if ( bFound ) + { + if ( pDim->GetDupFlag() ) + { + String aNewName = pDim->GetName(); + + // different name for each duplication of a (real) dimension... + for (long j=0; j<=i; j++) //! Test !!!!!! + aNewName += '*'; //! modify name at creation of SaveDimension + + uno::Reference<util::XCloneable> xCloneable( xIntDim, uno::UNO_QUERY ); + DBG_ASSERT( xCloneable.is(), "cannot clone dimension" ); + if (xCloneable.is()) + { + uno::Reference<util::XCloneable> xNew = xCloneable->createClone(); + uno::Reference<container::XNamed> xNewName( xNew, uno::UNO_QUERY ); + if (xNewName.is()) + { + xNewName->setName( aNewName ); + pDim->WriteToSource( xNew ); + } + } + } + else + pDim->WriteToSource( xIntDim ); + } + } + DBG_ASSERT(bFound, "WriteToSource: Dimension not found"); + } + + if ( xSourceProp.is() ) + { + if ( nColumnGrandMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xSourceProp, + rtl::OUString::createFromAscii(DP_PROP_COLUMNGRAND), (BOOL)nColumnGrandMode ); + if ( nRowGrandMode != SC_DPSAVEMODE_DONTKNOW ) + lcl_SetBoolProperty( xSourceProp, + rtl::OUString::createFromAscii(DP_PROP_ROWGRAND), (BOOL)nRowGrandMode ); + } + } + catch(uno::Exception&) + { + DBG_ERROR("exception in WriteToSource"); + } +} + +BOOL ScDPSaveData::IsEmpty() const +{ + long nCount = aDimList.Count(); + for (long i=0; i<nCount; i++) + { + ScDPSaveDimension* pDim = (ScDPSaveDimension*)aDimList.GetObject(i); + if ( pDim->GetOrientation() != sheet::DataPilotFieldOrientation_HIDDEN && !pDim->IsDataLayout() ) + return FALSE; + } + return TRUE; // no entries that are not hidden +} + +ScDPDimensionSaveData* ScDPSaveData::GetDimensionData() +{ + if (!pDimensionData) + pDimensionData = new ScDPDimensionSaveData; + return pDimensionData; +} + +void ScDPSaveData::SetDimensionData( const ScDPDimensionSaveData* pNew ) +{ + delete pDimensionData; + if ( pNew ) + pDimensionData = new ScDPDimensionSaveData( *pNew ); + else + pDimensionData = NULL; +} + diff --git a/sc/source/core/data/dpsdbtab.cxx b/sc/source/core/data/dpsdbtab.cxx new file mode 100644 index 000000000000..cfc98af5af99 --- /dev/null +++ b/sc/source/core/data/dpsdbtab.cxx @@ -0,0 +1,321 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpsdbtab.cxx,v $ + * $Revision: 1.15 $ + * + * 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 <tools/debug.hxx> +#include <vcl/msgbox.hxx> +#include <svtools/zforlist.hxx> +#include <comphelper/processfactory.hxx> +#include <comphelper/types.hxx> + +#include <com/sun/star/sheet/DataImportMode.hpp> +#include <com/sun/star/beans/XPropertySet.hpp> +#include <com/sun/star/sdb/CommandType.hpp> +#include <com/sun/star/sdb/XCompletedExecution.hpp> +#include <com/sun/star/sdbc/DataType.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/sdbc/XRowSet.hpp> +#include <com/sun/star/sdbc/XResultSetMetaDataSupplier.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> + +#include "dpsdbtab.hxx" +#include "collect.hxx" +#include "global.hxx" +#include "globstr.hrc" +#include "dpcachetable.hxx" +#include "dptabres.hxx" +#include "document.hxx" +#include "dpobject.hxx" + +using namespace com::sun::star; + +using ::std::vector; +using ::std::hash_map; +using ::std::hash_set; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::UNO_QUERY; + +#define SC_SERVICE_ROWSET "com.sun.star.sdb.RowSet" +#define SC_SERVICE_INTHANDLER "com.sun.star.sdb.InteractionHandler" + +//! move to a header file? +#define SC_DBPROP_DATASOURCENAME "DataSourceName" +#define SC_DBPROP_COMMAND "Command" +#define SC_DBPROP_COMMANDTYPE "CommandType" + +// ----------------------------------------------------------------------- + +class ScDatabaseDPData_Impl +{ +public: + ::com::sun::star::uno::Reference< ::com::sun::star::lang::XMultiServiceFactory > xServiceManager; + ScImportSourceDesc aDesc; + long nColCount; + uno::Reference<sdbc::XRowSet> xRowSet; + sal_Int32* pTypes; + SvNumberFormatter* pFormatter; + + ScDPCacheTable aCacheTable; + + ScDatabaseDPData_Impl(ScDPCollection* p) : + aCacheTable(p) + { + } +}; + +// ----------------------------------------------------------------------- + +ScDatabaseDPData::ScDatabaseDPData( + ScDocument* pDoc, + const ScImportSourceDesc& rImport ) : + ScDPTableData(pDoc) +{ + pImpl = new ScDatabaseDPData_Impl(pDoc->GetDPCollection()); + pImpl->xServiceManager = pDoc->GetServiceManager(); + pImpl->aDesc = rImport; + pImpl->nColCount = 0; + pImpl->pTypes = NULL; + pImpl->pFormatter = NULL; // created on demand + + OpenDatabase(); + CreateCacheTable(); +} + +ScDatabaseDPData::~ScDatabaseDPData() +{ + ::comphelper::disposeComponent( pImpl->xRowSet ); + + delete[] pImpl->pTypes; + delete pImpl->pFormatter; // NumberFormatter is local for this object + delete pImpl; +} + +void ScDatabaseDPData::DisposeData() +{ + //! use OpenDatabase here? + pImpl->aCacheTable.clear(); +} + +BOOL ScDatabaseDPData::OpenDatabase() +{ + sal_Int32 nSdbType = -1; + switch ( pImpl->aDesc.nType ) + { + case sheet::DataImportMode_SQL: nSdbType = sdb::CommandType::COMMAND; break; + case sheet::DataImportMode_TABLE: nSdbType = sdb::CommandType::TABLE; break; + case sheet::DataImportMode_QUERY: nSdbType = sdb::CommandType::QUERY; break; + default: + return FALSE; + } + + BOOL bSuccess = FALSE; + try + { + pImpl->xRowSet = uno::Reference<sdbc::XRowSet>( + comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString::createFromAscii( SC_SERVICE_ROWSET ) ), + uno::UNO_QUERY); + uno::Reference<beans::XPropertySet> xRowProp( pImpl->xRowSet, uno::UNO_QUERY ); + DBG_ASSERT( xRowProp.is(), "can't get RowSet" ); + if ( xRowProp.is() ) + { + // + // set source parameters + // + + uno::Any aAny; + + aAny <<= rtl::OUString( pImpl->aDesc.aDBName ); + xRowProp->setPropertyValue( + rtl::OUString::createFromAscii(SC_DBPROP_DATASOURCENAME), aAny ); + + aAny <<= rtl::OUString( pImpl->aDesc.aObject ); + xRowProp->setPropertyValue( + rtl::OUString::createFromAscii(SC_DBPROP_COMMAND), aAny ); + + aAny <<= nSdbType; + xRowProp->setPropertyValue( + rtl::OUString::createFromAscii(SC_DBPROP_COMMANDTYPE), aAny ); + + uno::Reference<sdb::XCompletedExecution> xExecute( pImpl->xRowSet, uno::UNO_QUERY ); + if ( xExecute.is() ) + { + uno::Reference<task::XInteractionHandler> xHandler( + comphelper::getProcessServiceFactory()->createInstance( + rtl::OUString::createFromAscii( SC_SERVICE_INTHANDLER ) ), + uno::UNO_QUERY); + xExecute->executeWithCompletion( xHandler ); + } + else + pImpl->xRowSet->execute(); + + // + // get column descriptions + // + + pImpl->nColCount = 0; + uno::Reference<sdbc::XResultSetMetaData> xMeta; + uno::Reference<sdbc::XResultSetMetaDataSupplier> xMetaSupp( pImpl->xRowSet, uno::UNO_QUERY ); + if ( xMetaSupp.is() ) + xMeta = xMetaSupp->getMetaData(); + if ( xMeta.is() ) + pImpl->nColCount = xMeta->getColumnCount(); // this is the number of real columns + + uno::Reference<sdbc::XResultSet> xResSet( pImpl->xRowSet, uno::UNO_QUERY ); + if ( pImpl->nColCount > 0 && xResSet.is() ) + { + pImpl->pTypes = new sal_Int32[pImpl->nColCount]; + for (long nCol=0; nCol<pImpl->nColCount; nCol++) + pImpl->pTypes[nCol] = xMeta->getColumnType( nCol+1 ); + + bSuccess = TRUE; + } + } + } + catch ( sdbc::SQLException& rError ) + { + //! store error message + InfoBox aInfoBox( 0, String(rError.Message) ); + aInfoBox.Execute(); + } + catch ( uno::Exception& ) + { + DBG_ERROR("Unexpected exception in database"); + } + + + if (!bSuccess) + ::comphelper::disposeComponent( pImpl->xRowSet ); + + return bSuccess; +} + +long ScDatabaseDPData::GetColumnCount() +{ + return pImpl->nColCount; +} + +const TypedScStrCollection& ScDatabaseDPData::GetColumnEntries(long nColumn) +{ + CreateCacheTable(); + return pImpl->aCacheTable.getFieldEntries(nColumn); +} + +String ScDatabaseDPData::getDimensionName(long nColumn) +{ + if (getIsDataLayoutDimension(nColumn)) + { + //! different internal and display names? + //return "Data"; + return ScGlobal::GetRscString(STR_PIVOT_DATA); + } + + CreateCacheTable(); + const String* pStr = pImpl->aCacheTable.getFieldName(nColumn); + if (pStr) + return *pStr; + + DBG_ERROR("getDimensionName: invalid dimension"); + return String(); +} + +BOOL ScDatabaseDPData::getIsDataLayoutDimension(long nColumn) +{ + return ( nColumn == pImpl->nColCount ); +} + +BOOL ScDatabaseDPData::IsDateDimension(long /* nDim */) +{ + //! later... + return FALSE; +} + +void ScDatabaseDPData::SetEmptyFlags( BOOL /* bIgnoreEmptyRows */, BOOL /* bRepeatIfEmpty */ ) +{ + // not used for database data + //! disable flags +} + +void ScDatabaseDPData::CreateCacheTable() +{ + if (!pImpl->aCacheTable.empty()) + return; + + // Get null date. + if (!pImpl->pFormatter) + pImpl->pFormatter = new SvNumberFormatter(pImpl->xServiceManager, ScGlobal::eLnge); + + pImpl->aCacheTable.fillTable(pImpl->xRowSet, *pImpl->pFormatter->GetNullDate()); +} + +void ScDatabaseDPData::FilterCacheTable(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims) +{ + CreateCacheTable(); + pImpl->aCacheTable.filterByPageDimension( + rCriteria, (IsRepeatIfEmpty() ? rCatDims : hash_set<sal_Int32>())); +} + +void ScDatabaseDPData::GetDrillDownData(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData) +{ + CreateCacheTable(); + sal_Int32 nRowSize = pImpl->aCacheTable.getRowSize(); + if (!nRowSize) + return; + + pImpl->aCacheTable.filterTable( + rCriteria, rData, IsRepeatIfEmpty() ? rCatDims : hash_set<sal_Int32>()); +} + +void ScDatabaseDPData::CalcResults(CalcInfo& rInfo, bool bAutoShow) +{ + CreateCacheTable(); + CalcResultsFromCacheTable(pImpl->aCacheTable, rInfo, bAutoShow); +} + +const ScDPCacheTable& ScDatabaseDPData::GetCacheTable() const +{ + return pImpl->aCacheTable; +} + +// ----------------------------------------------------------------------- + + + + + diff --git a/sc/source/core/data/dpshttab.cxx b/sc/source/core/data/dpshttab.cxx new file mode 100644 index 000000000000..7956b68ad03f --- /dev/null +++ b/sc/source/core/data/dpshttab.cxx @@ -0,0 +1,302 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dpshttab.cxx,v $ + * $Revision: 1.12 $ + * + * 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 <tools/debug.hxx> +#include <svtools/zforlist.hxx> + +#include "dpshttab.hxx" +#include "dptabres.hxx" +#include "document.hxx" +#include "collect.hxx" +#include "cell.hxx" +#include "dpcachetable.hxx" +#include "dpobject.hxx" +#include "globstr.hrc" + +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> + +#include <vector> +#include <set> + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Any; +using ::com::sun::star::uno::Sequence; +using ::std::vector; +using ::std::hash_map; +using ::std::hash_set; + +// ----------------------------------------------------------------------- + +class ScSheetDPData_Impl +{ +public: + ScDocument* pDoc; + ScRange aRange; + ScQueryParam aQuery; + BOOL* pSpecial; // to flag special handling of query parameters in ValidQuery. + BOOL bIgnoreEmptyRows; + BOOL bRepeatIfEmpty; + BOOL* pDateDim; + SCROW nNextRow; // for iterator, within range + + ScDPCacheTable aCacheTable; + + ScSheetDPData_Impl(ScDPCollection* p) : + pSpecial(NULL), + aCacheTable(p) + { + } +}; + +// ----------------------------------------------------------------------- + +ScSheetDPData::ScSheetDPData( ScDocument* pD, const ScSheetSourceDesc& rDesc ) : + ScDPTableData(pD) +{ + pImpl = new ScSheetDPData_Impl(pD->GetDPCollection()); + pImpl->pDoc = pD; + pImpl->aRange = rDesc.aSourceRange; + pImpl->aQuery = rDesc.aQueryParam; + pImpl->bIgnoreEmptyRows = FALSE; + pImpl->bRepeatIfEmpty = FALSE; + pImpl->pDateDim = NULL; + + pImpl->nNextRow = pImpl->aRange.aStart.Row() + 1; + + SCSIZE nEntryCount(pImpl->aQuery.GetEntryCount()); + pImpl->pSpecial = new BOOL[nEntryCount]; + for (SCSIZE j = 0; j < nEntryCount; ++j ) + { + ScQueryEntry& rEntry = pImpl->aQuery.GetEntry(j); + if (rEntry.bDoQuery) + { + pImpl->pSpecial[j] = false; + if (!rEntry.bQueryByString) + { + if (*rEntry.pStr == EMPTY_STRING && + ((rEntry.nVal == SC_EMPTYFIELDS) || (rEntry.nVal == SC_NONEMPTYFIELDS))) + pImpl->pSpecial[j] = true; + } + else + { + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = + !(pD->GetFormatTable()-> + IsNumberFormat(*rEntry.pStr, nIndex, rEntry.nVal)); + } + } + } +} + +ScSheetDPData::~ScSheetDPData() +{ + delete[] pImpl->pDateDim; + delete[] pImpl->pSpecial; + delete pImpl; +} + +void ScSheetDPData::DisposeData() +{ + pImpl->aCacheTable.clear(); +} + +long ScSheetDPData::GetColumnCount() +{ + CreateCacheTable(); + return pImpl->aCacheTable.getColSize(); +} + +const TypedScStrCollection& ScSheetDPData::GetColumnEntries(long nColumn) +{ + DBG_ASSERT(nColumn>=0 && nColumn < pImpl->aCacheTable.getColSize(), "ScSheetDPData: wrong column"); + CreateCacheTable(); + return pImpl->aCacheTable.getFieldEntries(nColumn); +} + +String ScSheetDPData::getDimensionName(long nColumn) +{ + CreateCacheTable(); + if (getIsDataLayoutDimension(nColumn)) + { + //! different internal and display names? + //return "Data"; + return ScGlobal::GetRscString(STR_PIVOT_DATA); + } + else if (nColumn >= pImpl->aCacheTable.getColSize()) + { + DBG_ERROR("getDimensionName: invalid dimension"); + return String(); + } + else + { + const String* pStr = pImpl->aCacheTable.getFieldName(nColumn); + if (pStr) + return *pStr; + else return String(); + } +} + +BOOL lcl_HasDateFormat( ScDocument* pDoc, const ScRange& rRange ) +{ + //! iterate formats in range? + + ScAddress aPos = rRange.aStart; + aPos.SetRow( aPos.Row() + 1 ); // below title + ULONG nFormat = pDoc->GetNumberFormat( aPos ); + SvNumberFormatter* pFormatter = pDoc->GetFormatTable(); + return ( pFormatter->GetType(nFormat) & NUMBERFORMAT_DATE ) != 0; +} + +BOOL ScSheetDPData::IsDateDimension(long nDim) +{ + CreateCacheTable(); + long nColCount = pImpl->aCacheTable.getColSize(); + if (getIsDataLayoutDimension(nDim)) + { + return FALSE; + } + else if (nDim >= nColCount) + { + DBG_ERROR("IsDateDimension: invalid dimension"); + return FALSE; + } + else + { + if (!pImpl->pDateDim) + { + pImpl->pDateDim = new BOOL[nColCount]; + ScRange aTestRange = pImpl->aRange; + for (long i = 0; i < nColCount; ++i) + { + SCCOL nCol = (SCCOL)( pImpl->aRange.aStart.Col() + i ); + aTestRange.aStart.SetCol(nCol); + aTestRange.aEnd.SetCol(nCol); + pImpl->pDateDim[i] = lcl_HasDateFormat( pImpl->pDoc, aTestRange ); + } + } + return pImpl->pDateDim[nDim]; + } +} + +UINT32 ScSheetDPData::GetNumberFormat(long nDim) +{ + CreateCacheTable(); + if (getIsDataLayoutDimension(nDim)) + { + return 0; + } + else if (nDim >= pImpl->aCacheTable.getColSize()) + { + DBG_ERROR("GetNumberFormat: invalid dimension"); + return 0; + } + else + { + // is queried only once per dimension from ScDPOutput -> no need to cache + + ScAddress aPos = pImpl->aRange.aStart; + aPos.SetCol( sal::static_int_cast<SCCOL>( aPos.Col() + nDim ) ); + aPos.SetRow( aPos.Row() + 1 ); // below title + return pImpl->pDoc->GetNumberFormat( aPos ); + } +} + +BOOL ScSheetDPData::getIsDataLayoutDimension(long nColumn) +{ + CreateCacheTable(); + return (nColumn == pImpl->aCacheTable.getColSize()); +} + +void ScSheetDPData::SetEmptyFlags( BOOL bIgnoreEmptyRows, BOOL bRepeatIfEmpty ) +{ + pImpl->bIgnoreEmptyRows = bIgnoreEmptyRows; + pImpl->bRepeatIfEmpty = bRepeatIfEmpty; +} + +bool ScSheetDPData::IsRepeatIfEmpty() +{ + return pImpl->bRepeatIfEmpty; +} + +void ScSheetDPData::CreateCacheTable() +{ + // Scan and store the data from the source range. + if (!pImpl->aCacheTable.empty()) + // already cached. + return; + + pImpl->aCacheTable.fillTable(pImpl->pDoc, pImpl->aRange, pImpl->aQuery, pImpl->pSpecial, + pImpl->bIgnoreEmptyRows); +} + +void ScSheetDPData::FilterCacheTable(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims) +{ + CreateCacheTable(); + pImpl->aCacheTable.filterByPageDimension( + rCriteria, (IsRepeatIfEmpty() ? rCatDims : hash_set<sal_Int32>())); +} + +void ScSheetDPData::GetDrillDownData(const vector<ScDPCacheTable::Criterion>& rCriteria, const hash_set<sal_Int32>& rCatDims, Sequence< Sequence<Any> >& rData) +{ + CreateCacheTable(); + sal_Int32 nRowSize = pImpl->aCacheTable.getRowSize(); + if (!nRowSize) + return; + + pImpl->aCacheTable.filterTable( + rCriteria, rData, IsRepeatIfEmpty() ? rCatDims : hash_set<sal_Int32>()); +} + +void ScSheetDPData::CalcResults(CalcInfo& rInfo, bool bAutoShow) +{ + CreateCacheTable(); + CalcResultsFromCacheTable(pImpl->aCacheTable, rInfo, bAutoShow); +} + +const ScDPCacheTable& ScSheetDPData::GetCacheTable() const +{ + return pImpl->aCacheTable; +} + +// ----------------------------------------------------------------------- + + + + + + + diff --git a/sc/source/core/data/dptabdat.cxx b/sc/source/core/data/dptabdat.cxx new file mode 100644 index 000000000000..41e44f84b647 --- /dev/null +++ b/sc/source/core/data/dptabdat.cxx @@ -0,0 +1,336 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dptabdat.cxx,v $ + * $Revision: 1.18 $ + * + * 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 <stdio.h> +#include <rtl/math.hxx> +#include <tools/debug.hxx> +#include <tools/date.hxx> +#include <unotools/transliterationwrapper.hxx> +#include <unotools/collatorwrapper.hxx> + +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> + +#include "dptabdat.hxx" +#include "global.hxx" +#include "dpcachetable.hxx" +#include "dptabres.hxx" +#include "document.hxx" +#include "dpobject.hxx" + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::std::vector; +using ::std::set; +using ::std::hash_map; + +// ----------------------------------------------------------------------- + +BOOL ScDPItemData::IsCaseInsEqual( const ScDPItemData& r ) const +{ + //! pass Transliteration? + //! inline? + return bHasValue ? ( r.bHasValue && rtl::math::approxEqual( fValue, r.fValue ) ) : + ( !r.bHasValue && + ScGlobal::GetpTransliteration()->isEqual( aString, r.aString ) ); +} + +size_t ScDPItemData::Hash() const +{ + if ( bHasValue ) + return (size_t) rtl::math::approxFloor( fValue ); + else + // If we do unicode safe case insensitive hash we can drop + // ScDPItemData::operator== and use ::IsCasInsEqual + return rtl_ustr_hashCode_WithLength( aString.GetBuffer(), aString.Len() ); +} + +BOOL ScDPItemData::operator==( const ScDPItemData& r ) const +{ + if ( bHasValue ) + { + if ( r.bHasValue ) + return rtl::math::approxEqual( fValue, r.fValue ); + else + return FALSE; + } + else if ( r.bHasValue ) + return FALSE; + else + // need exact equality until we have a safe case insensitive string hash + return aString == r.aString; +} + +sal_Int32 ScDPItemData::Compare( const ScDPItemData& rA, + const ScDPItemData& rB ) +{ + if ( rA.bHasValue ) + { + if ( rB.bHasValue ) + { + if ( rtl::math::approxEqual( rA.fValue, rB.fValue ) ) + return 0; + else if ( rA.fValue < rB.fValue ) + return -1; + else + return 1; + } + else + return -1; // values first + } + else if ( rB.bHasValue ) + return 1; // values first + else + return ScGlobal::GetCollator()->compareString( rA.aString, rB.aString ); +} + +// --------------------------------------------------------------------------- + +ScDPTableData::CalcInfo::CalcInfo() : + bRepeatIfEmpty(false) +{ +} + +// --------------------------------------------------------------------------- + +ScDPTableData::ScDPTableData(ScDocument* pDoc) : + mrSharedString(pDoc->GetDPCollection()->GetSharedString()) +{ + nLastDateVal = nLastHier = nLastLevel = nLastRet = -1; // invalid + + //! reset before new calculation (in case the base date is changed) +} + +ScDPTableData::~ScDPTableData() +{ +} + +long ScDPTableData::GetDatePart( long nDateVal, long nHierarchy, long nLevel ) +{ + if ( nDateVal == nLastDateVal && nHierarchy == nLastHier && nLevel == nLastLevel ) + return nLastRet; + + Date aDate( 30,12,1899 ); //! get from source data (and cache here) + aDate += nDateVal; + + long nRet = 0; + switch (nHierarchy) + { + case SC_DAPI_HIERARCHY_QUARTER: + switch (nLevel) + { + case 0: nRet = aDate.GetYear(); break; + case 1: nRet = (aDate.GetMonth()-1) / 3 + 1; break; + case 2: nRet = aDate.GetMonth(); break; + case 3: nRet = aDate.GetDay(); break; + default: + DBG_ERROR("GetDatePart: wrong level"); + } + break; + case SC_DAPI_HIERARCHY_WEEK: + switch (nLevel) + { + //! use settings for different definitions + case 0: nRet = aDate.GetYear(); break; //!... + case 1: nRet = aDate.GetWeekOfYear(); break; + case 2: nRet = (long)aDate.GetDayOfWeek(); break; + default: + DBG_ERROR("GetDatePart: wrong level"); + } + break; + default: + DBG_ERROR("GetDatePart: wrong hierarchy"); + } + + nLastDateVal = nDateVal; + nLastHier = nHierarchy; + nLastLevel = nLevel; + nLastRet = nRet; + + return nRet; +} + +bool ScDPTableData::IsRepeatIfEmpty() +{ + return false; +} + +UINT32 ScDPTableData::GetNumberFormat(long) +{ + return 0; // default format +} + +BOOL ScDPTableData::IsBaseForGroup(long) const +{ + return FALSE; // always false +} + +long ScDPTableData::GetGroupBase(long) const +{ + return -1; // always none +} + +BOOL ScDPTableData::IsNumOrDateGroup(long) const +{ + return FALSE; // always false +} + +BOOL ScDPTableData::IsInGroup( const ScDPItemData&, long, + const ScDPItemData&, long ) const +{ + DBG_ERROR("IsInGroup shouldn't be called for non-group data"); + return FALSE; +} + +BOOL ScDPTableData::HasCommonElement( const ScDPItemData&, long, + const ScDPItemData&, long ) const +{ + DBG_ERROR("HasCommonElement shouldn't be called for non-group data"); + return FALSE; +} + +ScSimpleSharedString& ScDPTableData::GetSharedString() +{ + return mrSharedString; +} + +void ScDPTableData::FillRowDataFromCacheTable(sal_Int32 nRow, const ScDPCacheTable& rCacheTable, + const CalcInfo& rInfo, CalcRowData& rData) +{ + // column dimensions + GetItemData(rCacheTable, nRow, rInfo.aColLevelDims, rData.aColData); + + // row dimensions + GetItemData(rCacheTable, nRow, rInfo.aRowLevelDims, rData.aRowData); + + // page dimensions + GetItemData(rCacheTable, nRow, rInfo.aPageDims, rData.aPageData); + + sal_Int32 n = rInfo.aDataSrcCols.size(); + for (sal_Int32 i = 0; i < n; ++i) + { + long nDim = rInfo.aDataSrcCols[i]; + rData.aValues.push_back( ScDPValueData() ); + ScDPValueData& rVal = rData.aValues.back(); + const ScDPCacheCell* pCell = rCacheTable.getCell( + static_cast<SCCOL>(nDim), static_cast<SCROW>(nRow), false); + if (pCell) + { + rVal.fValue = pCell->mbNumeric ? pCell->mfValue : 0.0; + rVal.nType = pCell->mnType; + } + else + rVal.Set(0.0, SC_VALTYPE_EMPTY); + } +} + +void ScDPTableData::ProcessRowData(CalcInfo& rInfo, CalcRowData& rData, bool bAutoShow) +{ + if (!bAutoShow) + { + rInfo.pColRoot->LateInitFrom(rInfo.aColDims, rInfo.aColLevels, rData.aColData, 0, *rInfo.pInitState); + rInfo.pRowRoot->LateInitFrom(rInfo.aRowDims, rInfo.aRowLevels, rData.aRowData, 0, *rInfo.pInitState); + } + + if ( ( !rInfo.pColRoot->GetChildDimension() || rInfo.pColRoot->GetChildDimension()->IsValidEntry(rData.aColData) ) && + ( !rInfo.pRowRoot->GetChildDimension() || rInfo.pRowRoot->GetChildDimension()->IsValidEntry(rData.aRowData) ) ) + { + //! single process method with ColMembers, RowMembers and data !!! + if (rInfo.pColRoot->GetChildDimension()) + { + vector<ScDPItemData> aEmptyData; + rInfo.pColRoot->GetChildDimension()->ProcessData(rData.aColData, NULL, aEmptyData, rData.aValues); + } + + rInfo.pRowRoot->ProcessData(rData.aRowData, rInfo.pColRoot->GetChildDimension(), + rData.aColData, rData.aValues); + } +} + +void ScDPTableData::CalcResultsFromCacheTable(const ScDPCacheTable& rCacheTable, CalcInfo& rInfo, bool bAutoShow) +{ + sal_Int32 nRowSize = rCacheTable.getRowSize(); + for (sal_Int32 nRow = 0; nRow < nRowSize; ++nRow) + { + if (!rCacheTable.isRowActive(nRow)) + continue; + + CalcRowData aData; + FillRowDataFromCacheTable(nRow, rCacheTable, rInfo, aData); + ProcessRowData(rInfo, aData, bAutoShow); + } +} + +void ScDPTableData::GetItemData(const ScDPCacheTable& rCacheTable, sal_Int32 nRow, + const vector<long>& rDims, vector<ScDPItemData>& rItemData) +{ + sal_Int32 nDimSize = rDims.size(); + for (sal_Int32 i = 0; i < nDimSize; ++i) + { + long nDim = rDims[i]; + rItemData.push_back( ScDPItemData() ); + ScDPItemData& rData = rItemData.back(); + if (getIsDataLayoutDimension(nDim)) + { + rData.SetString(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("x"))); + continue; + } + + const ScDPCacheCell* pCell = rCacheTable.getCell( + static_cast<SCCOL>(nDim), static_cast<SCROW>(nRow), IsRepeatIfEmpty()); + if (!pCell || pCell->mnType == SC_VALTYPE_EMPTY) + continue; + + const String* pString = GetSharedString().getString(pCell->mnStrId); + if (!pString) + continue; + + rData.aString = *pString; + rData.bHasValue = false; + if (pCell->mbNumeric) + { + rData.bHasValue = true; + rData.fValue = pCell->mfValue; + } + } +} + +// ----------------------------------------------------------------------- + + + + diff --git a/sc/source/core/data/dptabres.cxx b/sc/source/core/data/dptabres.cxx new file mode 100644 index 000000000000..398d4d1c7e01 --- /dev/null +++ b/sc/source/core/data/dptabres.cxx @@ -0,0 +1,3677 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dptabres.cxx,v $ + * $Revision: 1.14 $ + * + * 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 <tools/debug.hxx> +#include <rtl/math.hxx> + +#include "dptabdat.hxx" +#include "dptabres.hxx" +#include "dptabsrc.hxx" +#include "global.hxx" +#include "subtotal.hxx" +#include "globstr.hrc" +#include "datauno.hxx" // ScDataUnoConversion + +#include "document.hxx" // for DumpState only! + +#include <math.h> +#include <float.h> //! Test !!! +#include <algorithm> +#include <hash_map> + +#include <com/sun/star/sheet/DataResultFlags.hpp> +#include <com/sun/star/sheet/MemberResultFlags.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceItemType.hpp> +#include <com/sun/star/sheet/DataPilotFieldShowItemsMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> + +using namespace com::sun::star; +using ::std::vector; +using ::std::pair; +using ::std::hash_map; +using ::com::sun::star::uno::Sequence; + +// ----------------------------------------------------------------------- + +SV_IMPL_PTRARR( ScDPDataMembers, ScDPDataMemberPtr ); + +// ----------------------------------------------------------------------- + +static USHORT nFuncStrIds[12] = // passend zum enum ScSubTotalFunc +{ + 0, // SUBTOTAL_FUNC_NONE + STR_FUN_TEXT_AVG, // SUBTOTAL_FUNC_AVE + STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT + STR_FUN_TEXT_COUNT, // SUBTOTAL_FUNC_CNT2 + STR_FUN_TEXT_MAX, // SUBTOTAL_FUNC_MAX + STR_FUN_TEXT_MIN, // SUBTOTAL_FUNC_MIN + STR_FUN_TEXT_PRODUCT, // SUBTOTAL_FUNC_PROD + STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STD + STR_FUN_TEXT_STDDEV, // SUBTOTAL_FUNC_STDP + STR_FUN_TEXT_SUM, // SUBTOTAL_FUNC_SUM + STR_FUN_TEXT_VAR, // SUBTOTAL_FUNC_VAR + STR_FUN_TEXT_VAR // SUBTOTAL_FUNC_VARP +}; + +// ----------------------------------------------------------------------- + +// +// function objects for sorting of the column and row members: +// + +class ScDPRowMembersOrder +{ + ScDPResultDimension& rDimension; + long nMeasure; + BOOL bAscending; + +public: + ScDPRowMembersOrder( ScDPResultDimension& rDim, long nM, BOOL bAsc ) : + rDimension(rDim), + nMeasure(nM), + bAscending(bAsc) + {} + ~ScDPRowMembersOrder() {} + + BOOL operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; +}; + +class ScDPColMembersOrder +{ + ScDPDataDimension& rDimension; + long nMeasure; + BOOL bAscending; + +public: + ScDPColMembersOrder( ScDPDataDimension& rDim, long nM, BOOL bAsc ) : + rDimension(rDim), + nMeasure(nM), + bAscending(bAsc) + {} + ~ScDPColMembersOrder() {} + + BOOL operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; +}; + +static BOOL lcl_IsLess( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure, BOOL bAscending ) +{ + // members can be NULL if used for rows + + ScDPSubTotalState aEmptyState; + const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; + const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; + + BOOL bError1 = pAgg1 && pAgg1->HasError(); + BOOL bError2 = pAgg2 && pAgg2->HasError(); + if ( bError1 ) + { + if ( bError2 ) + return FALSE; // equal + else + return FALSE; // errors are always sorted at the end + } + else if ( bError2 ) + return TRUE; // errors are always sorted at the end + else + { + double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 + double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; + + // compare values + // don't have to check approxEqual, as this is the only sort criterion + + return bAscending ? ( fVal1 < fVal2 ) : ( fVal1 > fVal2 ); + } +} + +static BOOL lcl_IsEqual( const ScDPDataMember* pDataMember1, const ScDPDataMember* pDataMember2, long nMeasure ) +{ + // members can be NULL if used for rows + + ScDPSubTotalState aEmptyState; + const ScDPAggData* pAgg1 = pDataMember1 ? pDataMember1->GetConstAggData( nMeasure, aEmptyState ) : NULL; + const ScDPAggData* pAgg2 = pDataMember2 ? pDataMember2->GetConstAggData( nMeasure, aEmptyState ) : NULL; + + BOOL bError1 = pAgg1 && pAgg1->HasError(); + BOOL bError2 = pAgg2 && pAgg2->HasError(); + if ( bError1 ) + { + if ( bError2 ) + return TRUE; // equal + else + return FALSE; + } + else if ( bError2 ) + return FALSE; + else + { + double fVal1 = ( pAgg1 && pAgg1->HasData() ) ? pAgg1->GetResult() : 0.0; // no data is sorted as 0 + double fVal2 = ( pAgg2 && pAgg2->HasData() ) ? pAgg2->GetResult() : 0.0; + + // compare values + // this is used to find equal data at the end of the AutoShow range, so approxEqual must be used + + return rtl::math::approxEqual( fVal1, fVal2 ); + } +} + +BOOL ScDPRowMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const +{ + const ScDPResultMember* pMember1 = rDimension.GetMember(nIndex1); + const ScDPResultMember* pMember2 = rDimension.GetMember(nIndex2); + + // GetDataRoot can be NULL if there was no data. + // IsVisible == FALSE can happen after AutoShow. + const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL; + const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL; + + return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); +} + +BOOL ScDPColMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const +{ + ScDPDataMember* pDataMember1 = rDimension.GetMember(nIndex1); + ScDPDataMember* pDataMember2 = rDimension.GetMember(nIndex2); + + if ( pDataMember1 && !pDataMember1->IsVisible() ) //! IsColVisible? + pDataMember1 = NULL; + if ( pDataMember2 && !pDataMember2->IsVisible() ) + pDataMember2 = NULL; + + return lcl_IsLess( pDataMember1, pDataMember2, nMeasure, bAscending ); +} + +// ----------------------------------------------------------------------- + +ScDPInitState::ScDPInitState() : + nCount( 0 ) +{ + pIndex = new long[SC_DAPI_MAXFIELDS]; + pData = new ScDPItemData[SC_DAPI_MAXFIELDS]; +} + +ScDPInitState::~ScDPInitState() +{ + delete[] pIndex; + delete[] pData; +} + +void ScDPInitState::AddMember( long nSourceIndex, const ScDPItemData& rName ) +{ + DBG_ASSERT( nCount < SC_DAPI_MAXFIELDS, "too many InitState members" ); + if ( nCount < SC_DAPI_MAXFIELDS ) + { + pIndex[nCount] = nSourceIndex; + pData[nCount] = rName; + ++nCount; + } +} + +void ScDPInitState::RemoveMember() +{ + DBG_ASSERT( nCount > 0, "RemoveColIndex without index" ); + if ( nCount > 0 ) + --nCount; +} + +const ScDPItemData* ScDPInitState::GetNameForIndex( long nIndexValue ) const +{ + for (long i=0; i<nCount; i++) + if ( pIndex[i] == nIndexValue ) + return &pData[i]; + + return NULL; // not found +} + +// ----------------------------------------------------------------------- + +void lcl_DumpRow( const String& rType, const String& rName, const ScDPAggData* pAggData, + ScDocument* pDoc, ScAddress& rPos ) +{ + SCCOL nCol = rPos.Col(); + SCROW nRow = rPos.Row(); + SCTAB nTab = rPos.Tab(); + pDoc->SetString( nCol++, nRow, nTab, rType ); + pDoc->SetString( nCol++, nRow, nTab, rName ); + while ( pAggData ) + { + pDoc->SetValue( nCol++, nRow, nTab, pAggData->GetResult() ); + pAggData = pAggData->GetExistingChild(); + } + rPos.SetRow( nRow + 1 ); +} + +void lcl_Indent( ScDocument* pDoc, SCROW nStartRow, const ScAddress& rPos ) +{ + SCCOL nCol = rPos.Col(); + SCTAB nTab = rPos.Tab(); + + String aString; + for (SCROW nRow = nStartRow; nRow < rPos.Row(); nRow++) + { + pDoc->GetString( nCol, nRow, nTab, aString ); + if ( aString.Len() ) + { + aString.InsertAscii( " ", 0 ); + pDoc->SetString( nCol, nRow, nTab, aString ); + } + } +} + +// ----------------------------------------------------------------------- + +ScDPRunningTotalState::ScDPRunningTotalState( ScDPResultMember* pColRoot, ScDPResultMember* pRowRoot ) : + pColResRoot( pColRoot ), + pRowResRoot( pRowRoot ), + nColIndexPos( 0 ), + nRowIndexPos( 0 ) +{ + pColVisible = new long[SC_DAPI_MAXFIELDS+1]; + pColIndexes = new long[SC_DAPI_MAXFIELDS+1]; + pRowVisible = new long[SC_DAPI_MAXFIELDS+1]; + pRowIndexes = new long[SC_DAPI_MAXFIELDS+1]; + pColIndexes[0] = -1; + pRowIndexes[0] = -1; +} + +ScDPRunningTotalState::~ScDPRunningTotalState() +{ + delete[] pColVisible; + delete[] pColIndexes; + delete[] pRowVisible; + delete[] pRowIndexes; +} + +void ScDPRunningTotalState::AddColIndex( long nVisible, long nSorted ) +{ + DBG_ASSERT( nColIndexPos < SC_DAPI_MAXFIELDS, "too many column indexes" ); + if ( nColIndexPos < SC_DAPI_MAXFIELDS ) + { + pColVisible[nColIndexPos] = nVisible; + pColIndexes[nColIndexPos] = nSorted; + pColVisible[nColIndexPos+1] = -1; + pColIndexes[nColIndexPos+1] = -1; + ++nColIndexPos; + } +} + +void ScDPRunningTotalState::AddRowIndex( long nVisible, long nSorted ) +{ + DBG_ASSERT( nRowIndexPos < SC_DAPI_MAXFIELDS, "too many row indexes" ); + if ( nRowIndexPos < SC_DAPI_MAXFIELDS ) + { + pRowVisible[nRowIndexPos] = nVisible; + pRowIndexes[nRowIndexPos] = nSorted; + pRowVisible[nRowIndexPos+1] = -1; + pRowIndexes[nRowIndexPos+1] = -1; + ++nRowIndexPos; + } +} + +void ScDPRunningTotalState::RemoveColIndex() +{ + DBG_ASSERT( nColIndexPos > 0, "RemoveColIndex without index" ); + if ( nColIndexPos > 0 ) + { + --nColIndexPos; + pColVisible[nColIndexPos] = -1; + pColIndexes[nColIndexPos] = -1; + } +} + +void ScDPRunningTotalState::RemoveRowIndex() +{ + DBG_ASSERT( nRowIndexPos > 0, "RemoveRowIndex without index" ); + if ( nRowIndexPos > 0 ) + { + --nRowIndexPos; + pRowVisible[nRowIndexPos] = -1; + pRowIndexes[nRowIndexPos] = -1; + } +} + +// ----------------------------------------------------------------------- + +ScDPRelativePos::ScDPRelativePos( long nBase, long nDir ) : + nBasePos( nBase ), + nDirection( nDir ) +{ +} + +// ----------------------------------------------------------------------- + +void ScDPAggData::Update( const ScDPValueData& rNext, ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) +{ + if (nCount<0) // error? + return; // nothing more... + + if ( rNext.nType == SC_VALTYPE_EMPTY ) + return; + + if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && + rSubState.eColForce != rSubState.eRowForce ) + return; + if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; + if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; + + if ( eFunc == SUBTOTAL_FUNC_NONE ) + return; + + if ( eFunc != SUBTOTAL_FUNC_CNT2 ) // CNT2 counts everything, incl. strings and errors + { + if ( rNext.nType == SC_VALTYPE_ERROR ) + { + nCount = -1; // -1 for error (not for CNT2) + return; + } + if ( rNext.nType == SC_VALTYPE_STRING ) + return; // ignore + } + + ++nCount; // for all functions + + switch (eFunc) + { + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_AVE: + if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) + nCount = -1; // -1 for error + break; + case SUBTOTAL_FUNC_PROD: + if ( nCount == 1 ) // copy first value (fVal is initialized to 0) + fVal = rNext.fValue; + else if ( !SubTotal::SafeMult( fVal, rNext.fValue ) ) + nCount = -1; // -1 for error + break; + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: + // nothing more than incrementing nCount + break; + case SUBTOTAL_FUNC_MAX: + if ( nCount == 1 || rNext.fValue > fVal ) + fVal = rNext.fValue; + break; + case SUBTOTAL_FUNC_MIN: + if ( nCount == 1 || rNext.fValue < fVal ) + fVal = rNext.fValue; + break; + case SUBTOTAL_FUNC_STD: + case SUBTOTAL_FUNC_STDP: + case SUBTOTAL_FUNC_VAR: + case SUBTOTAL_FUNC_VARP: + { + // fAux is used to sum up squares + if ( !SubTotal::SafePlus( fVal, rNext.fValue ) ) + nCount = -1; // -1 for error + double fAdd = rNext.fValue; + if ( !SubTotal::SafeMult( fAdd, rNext.fValue ) || + !SubTotal::SafePlus( fAux, fAdd ) ) + nCount = -1; // -1 for error + } + break; + default: + DBG_ERROR("invalid function"); + } +} + +void ScDPAggData::Calculate( ScSubTotalFunc eFunc, const ScDPSubTotalState& rSubState ) +{ + // calculate the original result + // (without reference value, used as the basis for reference value calculation) + + // called several times at the cross-section of several subtotals - don't calculate twice then + if ( IsCalculated() ) + return; + + if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eColForce; + if ( rSubState.eRowForce != SUBTOTAL_FUNC_NONE ) eFunc = rSubState.eRowForce; + + if ( eFunc == SUBTOTAL_FUNC_NONE ) // this happens when there is no data dimension + { + nCount = SC_DPAGG_RESULT_EMPTY; // make sure there's a valid state for HasData etc. + return; + } + + // check the error conditions for the selected function + + BOOL bError = FALSE; + switch (eFunc) + { + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_PROD: + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: + bError = ( nCount < 0 ); // only real errors + break; + + case SUBTOTAL_FUNC_AVE: + case SUBTOTAL_FUNC_MAX: + case SUBTOTAL_FUNC_MIN: + case SUBTOTAL_FUNC_STDP: + case SUBTOTAL_FUNC_VARP: + bError = ( nCount <= 0 ); // no data is an error + break; + + case SUBTOTAL_FUNC_STD: + case SUBTOTAL_FUNC_VAR: + bError = ( nCount < 2 ); // need at least 2 values + break; + + default: + DBG_ERROR("invalid function"); + } + + // calculate the selected function + + double fResult = 0.0; + if ( !bError ) + { + switch (eFunc) + { + case SUBTOTAL_FUNC_MAX: + case SUBTOTAL_FUNC_MIN: + case SUBTOTAL_FUNC_SUM: + case SUBTOTAL_FUNC_PROD: + // different error conditions are handled above + fResult = fVal; + break; + + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: + fResult = nCount; + break; + + case SUBTOTAL_FUNC_AVE: + if ( nCount > 0 ) + fResult = fVal / (double) nCount; + break; + + //! use safe mul for fVal * fVal + + case SUBTOTAL_FUNC_STD: + if ( nCount >= 2 ) + fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1)); + break; + case SUBTOTAL_FUNC_VAR: + if ( nCount >= 2 ) + fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)(nCount-1); + break; + case SUBTOTAL_FUNC_STDP: + if ( nCount > 0 ) + fResult = sqrt((fAux - fVal*fVal/(double)(nCount)) / (double)nCount); + break; + case SUBTOTAL_FUNC_VARP: + if ( nCount > 0 ) + fResult = (fAux - fVal*fVal/(double)(nCount)) / (double)nCount; + break; + default: + DBG_ERROR("invalid function"); + } + } + + BOOL bEmpty = ( nCount == 0 ); // no data + + // store the result + // Empty is checked first, so empty results are shown empty even for "average" etc. + // If these results should be treated as errors in reference value calculations, + // a separate state value (EMPTY_ERROR) is needed. + // Now, for compatibility, empty "average" results are counted as 0. + + if ( bEmpty ) + nCount = SC_DPAGG_RESULT_EMPTY; + else if ( bError ) + nCount = SC_DPAGG_RESULT_ERROR; + else + nCount = SC_DPAGG_RESULT_VALID; + + if ( bEmpty || bError ) + fResult = 0.0; // default, in case the state is later modified + +// fprintf(stdout, "ScDPAggData::Calculate: result = %g\n", fResult);fflush(stdout); + fVal = fResult; // used directly from now on + fAux = 0.0; // used for running total or original result of reference value +} + +BOOL ScDPAggData::IsCalculated() const +{ + return ( nCount <= SC_DPAGG_RESULT_EMPTY ); +} + +double ScDPAggData::GetResult() const +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + return fVal; // use calculated value +} + +BOOL ScDPAggData::HasError() const +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + return ( nCount == SC_DPAGG_RESULT_ERROR ); +} + +BOOL ScDPAggData::HasData() const +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + return ( nCount != SC_DPAGG_RESULT_EMPTY ); // values or error +} + +void ScDPAggData::SetResult( double fNew ) +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + fVal = fNew; // don't reset error flag +} + +void ScDPAggData::SetError() +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + nCount = SC_DPAGG_RESULT_ERROR; +} + +void ScDPAggData::SetEmpty( BOOL bSet ) +{ + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + if ( bSet ) + nCount = SC_DPAGG_RESULT_EMPTY; + else + nCount = SC_DPAGG_RESULT_VALID; +} + +double ScDPAggData::GetAuxiliary() const +{ + // after Calculate, fAux is used as auxiliary value for running totals and reference values + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + return fAux; +} + +void ScDPAggData::SetAuxiliary( double fNew ) +{ + // after Calculate, fAux is used as auxiliary value for running totals and reference values + DBG_ASSERT( IsCalculated(), "ScDPAggData not calculated" ); + + fAux = fNew; +} + +ScDPAggData* ScDPAggData::GetChild() +{ + if (!pChild) + pChild = new ScDPAggData; + return pChild; +} + +void ScDPAggData::Reset() +{ + fVal = 0.0; + fAux = 0.0; + nCount = SC_DPAGG_EMPTY; + delete pChild; + pChild = NULL; +} + +// ----------------------------------------------------------------------- + +ScDPRowTotals::ScDPRowTotals() : + bIsInColRoot( FALSE ) +{ +} + +ScDPRowTotals::~ScDPRowTotals() +{ +} + +ScDPAggData* lcl_GetChildTotal( ScDPAggData* pFirst, long nMeasure ) +{ + DBG_ASSERT( nMeasure >= 0, "GetColTotal: no measure" ); + + ScDPAggData* pAgg = pFirst; + long nSkip = nMeasure; + + // subtotal settings are ignored - colum/row totals exist once per measure + + for ( long nPos=0; nPos<nSkip; nPos++ ) + pAgg = pAgg->GetChild(); // column total is constructed empty - children need to be created + + if ( !pAgg->IsCalculated() ) + { + // for first use, simulate an empty calculation + ScDPSubTotalState aEmptyState; + pAgg->Calculate( SUBTOTAL_FUNC_SUM, aEmptyState ); + } + + return pAgg; +} + +ScDPAggData* ScDPRowTotals::GetRowTotal( long nMeasure ) +{ + return lcl_GetChildTotal( &aRowTotal, nMeasure ); +} + +ScDPAggData* ScDPRowTotals::GetGrandTotal( long nMeasure ) +{ + return lcl_GetChildTotal( &aGrandTotal, nMeasure ); +} + +// ----------------------------------------------------------------------- + +static ScSubTotalFunc lcl_GetForceFunc( const ScDPLevel* pLevel, long nFuncNo ) +{ + ScSubTotalFunc eRet = SUBTOTAL_FUNC_NONE; + if ( pLevel ) + { + //! direct access via ScDPLevel + + uno::Sequence<sheet::GeneralFunction> aSeq = pLevel->getSubTotals(); + long nSequence = aSeq.getLength(); + if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) + { + // For manual subtotals, "automatic" is added as first function. + // ScDPResultMember::GetSubTotalCount adds to the count, here NONE has to be + // returned as the first function then. + + --nFuncNo; // keep NONE for first (check below), move the other entries + } + + if ( nFuncNo >= 0 && nFuncNo < nSequence ) + { + sheet::GeneralFunction eUser = aSeq.getConstArray()[nFuncNo]; + if (eUser != sheet::GeneralFunction_AUTO) + eRet = ScDataUnoConversion::GeneralToSubTotal( eUser ); + } + } + return eRet; +} + +// ----------------------------------------------------------------------- + +ScDPResultData::ScDPResultData( ScDPSource* pSrc ) : //! Ref + pSource( pSrc ), + nMeasCount( 0 ), + pMeasFuncs( NULL ), + pMeasRefs( NULL ), + pMeasRefOrient( NULL ), + pMeasNames( NULL ), + bLateInit( FALSE ), + bDataAtCol( FALSE ), + bDataAtRow( FALSE ) +{ +} + +ScDPResultData::~ScDPResultData() +{ + delete[] pMeasFuncs; + delete[] pMeasRefs; + delete[] pMeasRefOrient; + delete[] pMeasNames; +} + +void ScDPResultData::SetMeasureData( long nCount, const ScSubTotalFunc* pFunctions, + const sheet::DataPilotFieldReference* pRefs, const USHORT* pRefOrient, + const String* pNames ) +{ + delete[] pMeasFuncs; + delete[] pMeasRefs; + delete[] pMeasRefOrient; + delete[] pMeasNames; + if ( nCount ) + { + nMeasCount = nCount; + pMeasFuncs = new ScSubTotalFunc[nCount]; + pMeasRefs = new sheet::DataPilotFieldReference[nCount]; + pMeasRefOrient = new USHORT[nCount]; + pMeasNames = new String[nCount]; + for (long i=0; i<nCount; i++) + { + pMeasFuncs[i] = pFunctions[i]; + pMeasRefs[i] = pRefs[i]; + pMeasRefOrient[i] = pRefOrient[i]; + pMeasNames[i] = pNames[i]; + } + } + else + { + // use one dummy measure + nMeasCount = 1; + pMeasFuncs = new ScSubTotalFunc[1]; + pMeasFuncs[0] = SUBTOTAL_FUNC_NONE; + pMeasRefs = new sheet::DataPilotFieldReference[1]; // default ctor is ok + pMeasRefOrient = new USHORT[1]; + pMeasRefOrient[0] = sheet::DataPilotFieldOrientation_HIDDEN; + pMeasNames = new String[1]; + pMeasNames[0] = ScGlobal::GetRscString( STR_EMPTYDATA ); + } +} + +void ScDPResultData::SetDataLayoutOrientation( USHORT nOrient ) +{ + bDataAtCol = ( nOrient == sheet::DataPilotFieldOrientation_COLUMN ); + bDataAtRow = ( nOrient == sheet::DataPilotFieldOrientation_ROW ); +} + +void ScDPResultData::SetLateInit( BOOL bSet ) +{ + bLateInit = bSet; +} + +long ScDPResultData::GetColStartMeasure() const +{ + if ( nMeasCount == 1 ) return 0; + return bDataAtCol ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; +} + +long ScDPResultData::GetRowStartMeasure() const +{ + if ( nMeasCount == 1 ) return 0; + return bDataAtRow ? SC_DPMEASURE_ALL : SC_DPMEASURE_ANY; +} + +ScSubTotalFunc ScDPResultData::GetMeasureFunction(long nMeasure) const +{ + DBG_ASSERT( pMeasFuncs && nMeasure < nMeasCount, "bumm" ); + return pMeasFuncs[nMeasure]; +} + +const sheet::DataPilotFieldReference& ScDPResultData::GetMeasureRefVal(long nMeasure) const +{ + DBG_ASSERT( pMeasRefs && nMeasure < nMeasCount, "bumm" ); + return pMeasRefs[nMeasure]; +} + +USHORT ScDPResultData::GetMeasureRefOrient(long nMeasure) const +{ + DBG_ASSERT( pMeasRefOrient && nMeasure < nMeasCount, "bumm" ); + return pMeasRefOrient[nMeasure]; +} + +String ScDPResultData::GetMeasureString(long nMeasure, BOOL bForce, ScSubTotalFunc eForceFunc) const +{ + // with bForce==TRUE, return function instead of "result" for single measure + // with eForceFunc != SUBTOTAL_FUNC_NONE, always use eForceFunc + + if ( nMeasure < 0 || ( nMeasCount == 1 && !bForce && eForceFunc == SUBTOTAL_FUNC_NONE ) ) + { + // for user-specified subtotal function with all measures, + // display only function name + if ( eForceFunc != SUBTOTAL_FUNC_NONE ) + return ScGlobal::GetRscString(nFuncStrIds[eForceFunc]); + + return ScGlobal::GetRscString(STR_TABLE_ERGEBNIS); + } + else + { + DBG_ASSERT( pMeasNames && nMeasure < nMeasCount, "bumm" ); + + String aRet; + ScSubTotalFunc eFunc = ( eForceFunc == SUBTOTAL_FUNC_NONE ) ? + GetMeasureFunction(nMeasure) : eForceFunc; + USHORT nId = nFuncStrIds[eFunc]; + if (nId) + { + aRet += ScGlobal::GetRscString(nId); // function name + aRet.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " - " )); + } + aRet += pMeasNames[nMeasure]; // field name + + return aRet; + } +} + +String ScDPResultData::GetMeasureDimensionName(long nMeasure) const +{ + if ( nMeasure < 0 ) + { + DBG_ERROR("GetMeasureDimensionName: negative"); + return String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("***")); + } + + return pSource->GetDataDimName( nMeasure ); +} + +BOOL ScDPResultData::IsBaseForGroup( long nDim ) const +{ + return pSource->GetData()->IsBaseForGroup( nDim ); +} + +long ScDPResultData::GetGroupBase( long nGroupDim ) const +{ + return pSource->GetData()->GetGroupBase( nGroupDim ); +} + +BOOL ScDPResultData::IsNumOrDateGroup( long nDim ) const +{ + return pSource->GetData()->IsNumOrDateGroup( nDim ); +} + +BOOL ScDPResultData::IsInGroup( const ScDPItemData& rGroupData, long nGroupIndex, + const ScDPItemData& rBaseData, long nBaseIndex ) const +{ + return pSource->GetData()->IsInGroup( rGroupData, nGroupIndex, rBaseData, nBaseIndex ); +} + +BOOL ScDPResultData::HasCommonElement( const ScDPItemData& rFirstData, long nFirstIndex, + const ScDPItemData& rSecondData, long nSecondIndex ) const +{ + return pSource->GetData()->HasCommonElement( rFirstData, nFirstIndex, rSecondData, nSecondIndex ); +} + +// ----------------------------------------------------------------------- + + +ScDPResultMember::ScDPResultMember( const ScDPResultData* pData, const ScDPDimension* pDim, + const ScDPLevel* pLev, const ScDPMember* pDesc, + BOOL bForceSub ) : + pResultData( pData ), + pParentDim( pDim ), + pParentLevel( pLev ), + pMemberDesc( pDesc ), + pChildDimension( NULL ), + pDataRoot( NULL ), + bHasElements( FALSE ), + bForceSubTotal( bForceSub ), + bHasHiddenDetails( FALSE ), + bInitialized( FALSE ), + bAutoHidden( FALSE ) +{ + // pParentLevel/pMemberDesc is 0 for root members +} + +ScDPResultMember::~ScDPResultMember() +{ + delete pChildDimension; + delete pDataRoot; +} + +String ScDPResultMember::GetName() const +{ + if (pMemberDesc) + return pMemberDesc->GetNameStr(); + else + return ScGlobal::GetRscString(STR_PIVOT_TOTAL); // root member +} + +void ScDPResultMember::FillItemData( ScDPItemData& rData ) const +{ + if (pMemberDesc) + pMemberDesc->FillItemData( rData ); + else + rData.SetString( ScGlobal::GetRscString(STR_PIVOT_TOTAL) ); // root member +} + +BOOL ScDPResultMember::IsNamedItem( const ScDPItemData& r ) const +{ + //! store ScDPMember pointer instead of ScDPMember ??? + + if (pMemberDesc) + return ((ScDPMember*)pMemberDesc)->IsNamedItem( r ); + return FALSE; +} + +bool ScDPResultMember::IsValidEntry( const vector<ScDPItemData>& aMembers ) const +{ + if ( !IsValid() ) + return false; + + const ScDPResultDimension* pChildDim = GetChildDimension(); + if (pChildDim) + { + if (aMembers.size() < 2) + return false; + + vector<ScDPItemData>::const_iterator itr = aMembers.begin(); + vector<ScDPItemData> aChildMembers(++itr, aMembers.end()); + return pChildDim->IsValidEntry(aChildMembers); + } + else + return true; +} + +void ScDPResultMember::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, + size_t nPos, ScDPInitState& rInitState ) +{ + // with LateInit, initialize only those members that have data + if ( pResultData->IsLateInit() ) + return; + + bInitialized = TRUE; + + if (nPos >= ppDim.size()) + return; + + // skip child dimension if details are not shown + if ( pMemberDesc && !pMemberDesc->getShowDetails() ) + { + bHasHiddenDetails = TRUE; // only if there is a next dimension + return; + } + + pChildDimension = new ScDPResultDimension( pResultData ); + pChildDimension->InitFrom( ppDim, ppLev, nPos, rInitState ); +} + +void ScDPResultMember::LateInitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, + const vector<ScDPItemData>& pItemData, size_t nPos, + ScDPInitState& rInitState ) +{ + // without LateInit, everything has already been initialized + if ( !pResultData->IsLateInit() ) + return; + + bInitialized = TRUE; + + if (nPos >= ppDim.size()) + // No next dimension. Bail out. + return; + + // skip child dimension if details are not shown + if ( pMemberDesc && !pMemberDesc->getShowDetails() ) + { + bHasHiddenDetails = TRUE; // only if there is a next dimension + return; + } + + // LateInitFrom is called several times... + if ( !pChildDimension ) + pChildDimension = new ScDPResultDimension( pResultData ); + + pChildDimension->LateInitFrom( ppDim, ppLev, pItemData, nPos, rInitState ); +} + +BOOL ScDPResultMember::IsSubTotalInTitle(long nMeasure) const +{ + BOOL bRet = FALSE; + if ( pChildDimension && pParentLevel && + pParentLevel->IsOutlineLayout() && pParentLevel->IsSubtotalsAtTop() ) + { + long nUserSubStart; + long nSubTotals = GetSubTotalCount( &nUserSubStart ); + nSubTotals -= nUserSubStart; // visible count + if ( nSubTotals ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nSubTotals *= pResultData->GetMeasureCount(); // number of subtotals that will be inserted + + // only a single subtotal row will be shown in the outline title row + if ( nSubTotals == 1 ) + bRet = TRUE; + } + } + return bRet; +} + +long ScDPResultMember::GetSize(long nMeasure) const +{ + if ( !IsVisible() ) + return 0; + + long nExtraSpace = 0; + if ( pParentLevel && pParentLevel->IsAddEmpty() ) + ++nExtraSpace; + + if ( pChildDimension ) + { + // outline layout takes up an extra row for the title only if subtotals aren't shown in that row + if ( pParentLevel && pParentLevel->IsOutlineLayout() && !IsSubTotalInTitle( nMeasure ) ) + ++nExtraSpace; + + long nSize = pChildDimension->GetSize(nMeasure); + long nUserSubStart; + long nUserSubCount = GetSubTotalCount( &nUserSubStart ); + nUserSubCount -= nUserSubStart; // for output size, use visible count + if ( nUserSubCount ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nSize += pResultData->GetMeasureCount() * nUserSubCount; + else + nSize += nUserSubCount; + } + return nSize + nExtraSpace; + } + else + { + if ( nMeasure == SC_DPMEASURE_ALL ) + return pResultData->GetMeasureCount() + nExtraSpace; + else + return 1 + nExtraSpace; + } +} + +BOOL ScDPResultMember::IsVisible() const +{ + // not initialized -> shouldn't be there at all + // (allocated only to preserve ordering) + + return ( bHasElements || ( pParentLevel && pParentLevel->getShowEmpty() ) ) && IsValid() && bInitialized; +} + +BOOL ScDPResultMember::IsValid() const +{ + // non-Valid members are left out of calculation + + // was member set no invisible at the DataPilotSource? + if ( pMemberDesc && !pMemberDesc->getIsVisible() ) + return FALSE; + + if ( bAutoHidden ) + return FALSE; + + return TRUE; +} + +BOOL ScDPResultMember::HasHiddenDetails() const +{ + // bHasHiddenDetails is set only if the "show details" flag is off, + // and there was a child dimension to skip + + return bHasHiddenDetails; +} + +long ScDPResultMember::GetSubTotalCount( long* pUserSubStart ) const +{ + if ( pUserSubStart ) + *pUserSubStart = 0; // default + + if ( bForceSubTotal ) // set if needed for root members + return 1; // grand total is always "automatic" + else if ( pParentLevel ) + { + //! direct access via ScDPLevel + + uno::Sequence<sheet::GeneralFunction> aSeq = pParentLevel->getSubTotals(); + long nSequence = aSeq.getLength(); + if ( nSequence && aSeq[0] != sheet::GeneralFunction_AUTO ) + { + // For manual subtotals, always add "automatic" as first function + // (used for calculation, but not for display, needed for sorting, see lcl_GetForceFunc) + + ++nSequence; + if ( pUserSubStart ) + *pUserSubStart = 1; // visible subtotals start at 1 + } + return nSequence; + } + else + return 0; +} + +void ScDPResultMember::ProcessData( const vector<ScDPItemData>& aChildMembers, const ScDPResultDimension* pDataDim, + const vector<ScDPItemData>& aDataMembers, const vector<ScDPValueData>& aValues ) +{ + SetHasElements(); + + if (pChildDimension) + pChildDimension->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); + + if ( !pDataRoot ) + { + pDataRoot = new ScDPDataMember( pResultData, NULL ); + if ( pDataDim ) + pDataRoot->InitFrom( pDataDim ); // recursive + } + + ScDPSubTotalState aSubState; // initial state + + long nUserSubCount = GetSubTotalCount(); + + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !pChildDimension ) + nUserSubCount = 1; + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + // #i68338# if nUserSubCount is 1 (automatic only), don't set nRowSubTotalFunc + if ( pChildDimension && nUserSubCount > 1 ) + { + aSubState.nRowSubTotalFunc = nUserPos; + aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); + } + + pDataRoot->ProcessData( aDataMembers, aValues, aSubState ); + } +} + +void ScDPResultMember::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, + long& rPos, long nMeasure, BOOL bRoot, + const String* pMemberName, + const String* pMemberCaption ) +{ + // IsVisible() test is in ScDPResultDimension::FillMemberResults + // (not on data layout dimension) + + long nSize = GetSize(nMeasure); + sheet::MemberResult* pArray = pSequences->getArray(); + DBG_ASSERT( rPos+nSize <= pSequences->getLength(), "bumm" ); + + BOOL bIsNumeric = FALSE; + String aName; + if ( pMemberName ) // if pMemberName != NULL, use instead of real member name + aName = *pMemberName; + else + { + ScDPItemData aItemData; + FillItemData( aItemData ); + aName = aItemData.aString; + bIsNumeric = aItemData.bHasValue; + } + + if ( bIsNumeric && pParentDim && pResultData->IsNumOrDateGroup( pParentDim->GetDimension() ) ) + { + // Numeric group dimensions use numeric entries for proper sorting, + // but the group titles must be output as text. + bIsNumeric = FALSE; + } + + String aCaption = aName; + if ( pMemberCaption ) // use pMemberCaption if != NULL + aCaption = *pMemberCaption; + if (!aCaption.Len()) + aCaption = ScGlobal::GetRscString(STR_EMPTYDATA); + + if ( !bIsNumeric ) + { + // add a "'" character so a string isn't parsed as value in the output cell + //! have a separate bit in Flags (MemberResultFlags) instead? + aCaption.Insert( (sal_Unicode) '\'', 0 ); + } + + if ( nSize && !bRoot ) // root is overwritten by first dimension + { + pArray[rPos].Name = rtl::OUString(aName); + pArray[rPos].Caption = rtl::OUString(aCaption); + pArray[rPos].Flags |= sheet::MemberResultFlags::HASMEMBER; + + // set "continue" flag (removed for subtotals later) + for (long i=1; i<nSize; i++) + pArray[rPos+i].Flags |= sheet::MemberResultFlags::CONTINUE; + } + + long nExtraSpace = 0; + if ( pParentLevel && pParentLevel->IsAddEmpty() ) + ++nExtraSpace; + + BOOL bTitleLine = FALSE; + if ( pParentLevel && pParentLevel->IsOutlineLayout() ) + bTitleLine = TRUE; + + // if the subtotals are shown at the top (title row) in outline layout, + // no extra row for the subtotals is needed + BOOL bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); + + BOOL bHasChild = ( pChildDimension != NULL ); + if (bHasChild) + { + if ( bTitleLine ) // in tabular layout the title is on a separate row + ++rPos; // -> fill child dimension one row below + + if (bRoot) // same sequence for root member + pChildDimension->FillMemberResults( pSequences, rPos, nMeasure ); + else + pChildDimension->FillMemberResults( pSequences + 1, rPos, nMeasure ); + + if ( bTitleLine ) // title row is included in GetSize, so the following + --rPos; // positions are calculated with the normal values + } + + rPos += nSize; + + long nUserSubStart; + long nUserSubCount = GetSubTotalCount(&nUserSubStart); + if ( nUserSubCount && pChildDimension && !bSubTotalInTitle ) + { + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + + rPos -= nSubSize * (nUserSubCount - nUserSubStart); // GetSize includes space for SubTotal + rPos -= nExtraSpace; // GetSize includes the empty line + + for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) + { + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + + ScSubTotalFunc eForce = SUBTOTAL_FUNC_NONE; + if (bHasChild) + eForce = lcl_GetForceFunc( pParentLevel, nUserPos ); + + String aSubStr = aName; //! caption? + aSubStr += ' '; + aSubStr += pResultData->GetMeasureString(nMemberMeasure, FALSE, eForce); + + pArray[rPos].Name = rtl::OUString(aName); + pArray[rPos].Caption = rtl::OUString(aSubStr); + pArray[rPos].Flags = ( pArray[rPos].Flags | + ( sheet::MemberResultFlags::HASMEMBER | sheet::MemberResultFlags::SUBTOTAL) ) & + ~sheet::MemberResultFlags::CONTINUE; + + if ( nMeasure == SC_DPMEASURE_ALL ) + { + // data layout dimension is (direct/indirect) child of this. + // data layout dimension must have name for all entries. + + uno::Sequence<sheet::MemberResult>* pLayoutSeq = pSequences; + if (!bRoot) + ++pLayoutSeq; + ScDPResultDimension* pLayoutDim = pChildDimension; + while ( pLayoutDim && !pLayoutDim->IsDataLayout() ) + { + pLayoutDim = pLayoutDim->GetFirstChildDimension(); + ++pLayoutSeq; + } + if ( pLayoutDim ) + { + sheet::MemberResult* pLayoutArray = pLayoutSeq->getArray(); + String aDataName = pResultData->GetMeasureDimensionName(nMemberMeasure); + pLayoutArray[rPos].Name = rtl::OUString(aDataName); + } + } + + rPos += 1; + } + } + + rPos += nExtraSpace; // add again (subtracted above) + } +} + +void ScDPResultMember::FillDataResults( const ScDPResultMember* pRefMember, + uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, + long& rRow, long nMeasure ) const +{ + // IsVisible() test is in ScDPResultDimension::FillDataResults + // (not on data layout dimension) + + long nStartRow = rRow; + + long nExtraSpace = 0; + if ( pParentLevel && pParentLevel->IsAddEmpty() ) + ++nExtraSpace; + + BOOL bTitleLine = FALSE; + if ( pParentLevel && pParentLevel->IsOutlineLayout() ) + bTitleLine = TRUE; + + BOOL bSubTotalInTitle = IsSubTotalInTitle( nMeasure ); + + BOOL bHasChild = ( pChildDimension != NULL ); + if (bHasChild) + { + if ( bTitleLine ) // in tabular layout the title is on a separate row + ++rRow; // -> fill child dimension one row below + + pChildDimension->FillDataResults( pRefMember, rSequence, rRow, nMeasure ); // doesn't modify rRow + rRow += (USHORT) GetSize( nMeasure ); + + if ( bTitleLine ) // title row is included in GetSize, so the following + --rRow; // positions are calculated with the normal values + } + + long nUserSubStart; + long nUserSubCount = GetSubTotalCount(&nUserSubStart); + if ( nUserSubCount || !bHasChild ) + { + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + { + nUserSubCount = 1; + nUserSubStart = 0; + } + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + if (bHasChild) + { + rRow -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal + rRow -= nExtraSpace; // GetSize includes the empty line + } + + long nMoveSubTotal = 0; + if ( bSubTotalInTitle ) + { + nMoveSubTotal = rRow - nStartRow; // force to first (title) row + rRow = nStartRow; + } + + if ( pDataRoot ) + { + ScDPSubTotalState aSubState; // initial state + + for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) + { + if ( bHasChild && nUserSubCount > 1 ) + { + aSubState.nRowSubTotalFunc = nUserPos; + aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) + nMemberMeasure = SC_DPMEASURE_ALL; + + DBG_ASSERT( rRow < rSequence.getLength(), "bumm" ); + uno::Sequence<sheet::DataResult>& rSubSeq = rSequence.getArray()[rRow]; + long nSeqCol = 0; + pDataRoot->FillDataRow( pRefMember, rSubSeq, nSeqCol, nMemberMeasure, bHasChild, aSubState ); + + rRow += 1; + } + } + } + else + rRow += nSubSize * ( nUserSubCount - nUserSubStart ); // empty rows occur when ShowEmpty is true + + // add extra space again if subtracted from GetSize above, + // add to own size if no children + rRow += nExtraSpace; + + rRow += nMoveSubTotal; + } +} + +void ScDPResultMember::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const +{ + // IsVisible() test is in ScDPResultDimension::FillDataResults + // (not on data layout dimension) + + BOOL bHasChild = ( pChildDimension != NULL ); + + long nUserSubCount = GetSubTotalCount(); + // process subtotals even if not shown +// if ( nUserSubCount || !bHasChild ) + { + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + nUserSubCount = 1; + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + + if ( pDataRoot ) + { + ScDPSubTotalState aSubState; // initial state + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + if ( bHasChild && nUserSubCount > 1 ) + { + aSubState.nRowSubTotalFunc = nUserPos; + aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) + nMemberMeasure = SC_DPMEASURE_ALL; + + pDataRoot->UpdateDataRow( pRefMember, nMemberMeasure, bHasChild, aSubState ); + } + } + } + } + + if (bHasChild) // child dimension must be processed last, so the column total is known + { + pChildDimension->UpdateDataResults( pRefMember, nMeasure ); + } +} + +void ScDPResultMember::SortMembers( ScDPResultMember* pRefMember ) +{ + BOOL bHasChild = ( pChildDimension != NULL ); + if (bHasChild) + pChildDimension->SortMembers( pRefMember ); // sorting is done at the dimension + + BOOL bIsRoot = ( pParentLevel == NULL ); + if ( bIsRoot && pDataRoot ) + { + // use the row root member to sort columns + // sub total count is always 1 + + pDataRoot->SortMembers( pRefMember ); + } +} + +void ScDPResultMember::DoAutoShow( ScDPResultMember* pRefMember ) +{ + BOOL bHasChild = ( pChildDimension != NULL ); + if (bHasChild) + pChildDimension->DoAutoShow( pRefMember ); // sorting is done at the dimension + + BOOL bIsRoot = ( pParentLevel == NULL ); + if ( bIsRoot && pDataRoot ) + { + // use the row root member to sort columns + // sub total count is always 1 + + pDataRoot->DoAutoShow( pRefMember ); + } +} + +void ScDPResultMember::ResetResults( BOOL bRoot ) +{ + if (pDataRoot) + pDataRoot->ResetResults(); + + if (pChildDimension) + pChildDimension->ResetResults(); + + if (!bRoot) + bHasElements = FALSE; +} + +void ScDPResultMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, + ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const +{ + // IsVisible() test is in ScDPResultDimension::FillDataResults + // (not on data layout dimension) + + BOOL bIsRoot = ( pParentLevel == NULL ); + rTotals.SetInColRoot( bIsRoot ); + + BOOL bHasChild = ( pChildDimension != NULL ); + + long nUserSubCount = GetSubTotalCount(); + if ( nUserSubCount || !bHasChild ) + { + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + nUserSubCount = 1; + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + + if ( pDataRoot ) + { + ScDPSubTotalState aSubState; // initial state + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + if ( bHasChild && nUserSubCount > 1 ) + { + aSubState.nRowSubTotalFunc = nUserPos; + aSubState.eRowForce = lcl_GetForceFunc( pParentLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + else if ( pResultData->GetColStartMeasure() == SC_DPMEASURE_ALL ) + nMemberMeasure = SC_DPMEASURE_ALL; + + pDataRoot->UpdateRunningTotals( pRefMember, nMemberMeasure, + bHasChild, aSubState, rRunning, rTotals, *this ); + } + } + } + } + + if (bHasChild) // child dimension must be processed last, so the column total is known + { + pChildDimension->UpdateRunningTotals( pRefMember, nMeasure, rRunning, rTotals ); + } +} + +void ScDPResultMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const +{ + lcl_DumpRow( String::CreateFromAscii("ScDPResultMember"), GetName(), NULL, pDoc, rPos ); + SCROW nStartRow = rPos.Row(); + + if (pDataRoot) + pDataRoot->DumpState( pRefMember, pDoc, rPos ); + + if (pChildDimension) + pChildDimension->DumpState( pRefMember, pDoc, rPos ); + + lcl_Indent( pDoc, nStartRow, rPos ); +} + +ScDPAggData* ScDPResultMember::GetColTotal( long nMeasure ) const +{ + return lcl_GetChildTotal( const_cast<ScDPAggData*>(&aColTotal), nMeasure ); +} + +void ScDPResultMember::FillVisibilityData(ScDPResultVisibilityData& rData) const +{ + if (pChildDimension) + pChildDimension->FillVisibilityData(rData); +} + +// ----------------------------------------------------------------------- + +ScDPDataMember::ScDPDataMember( const ScDPResultData* pData, const ScDPResultMember* pRes ) : + pResultData( pData ), + pResultMember( pRes ), + pChildDimension( NULL ) +{ + // pResultMember is 0 for root members +} + +ScDPDataMember::~ScDPDataMember() +{ + delete pChildDimension; +} + +String ScDPDataMember::GetName() const +{ + if (pResultMember) + return pResultMember->GetName(); + else + return EMPTY_STRING; +} + +BOOL ScDPDataMember::IsVisible() const +{ + if (pResultMember) + return pResultMember->IsVisible(); + else + return FALSE; +} + +BOOL ScDPDataMember::IsNamedItem( const ScDPItemData& r ) const +{ + if (pResultMember) + return pResultMember->IsNamedItem(r); + else + return FALSE; +} + +BOOL ScDPDataMember::HasHiddenDetails() const +{ + if (pResultMember) + return pResultMember->HasHiddenDetails(); + else + return FALSE; +} + +void ScDPDataMember::InitFrom( const ScDPResultDimension* pDim ) +{ + if ( !pChildDimension ) + pChildDimension = new ScDPDataDimension(pResultData); + pChildDimension->InitFrom(pDim); +} + +const long SC_SUBTOTALPOS_AUTO = -1; // default +const long SC_SUBTOTALPOS_SKIP = -2; // don't use + +long lcl_GetSubTotalPos( const ScDPSubTotalState& rSubState ) +{ + if ( rSubState.nColSubTotalFunc >= 0 && rSubState.nRowSubTotalFunc >= 0 && + rSubState.nColSubTotalFunc != rSubState.nRowSubTotalFunc ) + { + // #i68338# don't return the same index for different combinations (leading to repeated updates), + // return a "don't use" value instead + + return SC_SUBTOTALPOS_SKIP; + } + + long nRet = SC_SUBTOTALPOS_AUTO; + if ( rSubState.nColSubTotalFunc >= 0 ) nRet = rSubState.nColSubTotalFunc; + if ( rSubState.nRowSubTotalFunc >= 0 ) nRet = rSubState.nRowSubTotalFunc; + return nRet; +} + +void ScDPDataMember::UpdateValues( const vector<ScDPValueData>& aValues, const ScDPSubTotalState& rSubState ) +{ + //! find out how many and which subtotals are used + + ScDPAggData* pAgg = &aAggregate; + + long nSubPos = lcl_GetSubTotalPos(rSubState); + if (nSubPos == SC_SUBTOTALPOS_SKIP) + return; + if (nSubPos > 0) + { + long nSkip = nSubPos * pResultData->GetMeasureCount(); + for (long i=0; i<nSkip; i++) + pAgg = pAgg->GetChild(); // created if not there + } + + size_t nCount = aValues.size(); + for (size_t nPos = 0; nPos < nCount; ++nPos) + { + pAgg->Update(aValues[nPos], pResultData->GetMeasureFunction(nPos), rSubState); + pAgg = pAgg->GetChild(); + } +} + +void ScDPDataMember::ProcessData( const vector<ScDPItemData>& aChildMembers, const vector<ScDPValueData>& aValues, + const ScDPSubTotalState& rSubState ) +{ + if ( pResultData->IsLateInit() && !pChildDimension && pResultMember && pResultMember->GetChildDimension() ) + { + // if this DataMember doesn't have a child dimension because the ResultMember's + // child dimension wasn't there yet during this DataMembers's creation, + // create the child dimension now + InitFrom( pResultMember->GetChildDimension() ); + } + + ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column + + long nUserSubCount = pResultMember ? pResultMember->GetSubTotalCount() : 0; + + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !pChildDimension ) + nUserSubCount = 1; + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + if ( pChildDimension && nUserSubCount > 1 ) + { + const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; + aLocalSubState.nColSubTotalFunc = nUserPos; + aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); + } + + UpdateValues( aValues, aLocalSubState ); + } + + if (pChildDimension) + pChildDimension->ProcessData( aChildMembers, aValues, rSubState ); // with unmodified subtotal state +} + +BOOL ScDPDataMember::HasData( long nMeasure, const ScDPSubTotalState& rSubState ) const +{ + if ( rSubState.eColForce != SUBTOTAL_FUNC_NONE && rSubState.eRowForce != SUBTOTAL_FUNC_NONE && + rSubState.eColForce != rSubState.eRowForce ) + return FALSE; + + // #74542# HasData can be different between measures! + + const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); + if (!pAgg) + return FALSE; //! error? + + return pAgg->HasData(); +} + +BOOL ScDPDataMember::HasError( long nMeasure, const ScDPSubTotalState& rSubState ) const +{ + const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); + if (!pAgg) + return TRUE; + + return pAgg->HasError(); +} + +double ScDPDataMember::GetAggregate( long nMeasure, const ScDPSubTotalState& rSubState ) const +{ + const ScDPAggData* pAgg = GetConstAggData( nMeasure, rSubState ); + if (!pAgg) + return DBL_MAX; //! error? + + return pAgg->GetResult(); +} + +ScDPAggData* ScDPDataMember::GetAggData( long nMeasure, const ScDPSubTotalState& rSubState ) +{ + DBG_ASSERT( nMeasure >= 0, "GetAggData: no measure" ); + + ScDPAggData* pAgg = &aAggregate; + long nSkip = nMeasure; + long nSubPos = lcl_GetSubTotalPos(rSubState); + if (nSubPos == SC_SUBTOTALPOS_SKIP) + return NULL; + if (nSubPos > 0) + nSkip += nSubPos * pResultData->GetMeasureCount(); + + for ( long nPos=0; nPos<nSkip; nPos++ ) + pAgg = pAgg->GetChild(); //! need to create children here? + + return pAgg; +} + +const ScDPAggData* ScDPDataMember::GetConstAggData( long nMeasure, const ScDPSubTotalState& rSubState ) const +{ + DBG_ASSERT( nMeasure >= 0, "GetConstAggData: no measure" ); + + const ScDPAggData* pAgg = &aAggregate; + long nSkip = nMeasure; + long nSubPos = lcl_GetSubTotalPos(rSubState); + if (nSubPos == SC_SUBTOTALPOS_SKIP) + return NULL; + if (nSubPos > 0) + nSkip += nSubPos * pResultData->GetMeasureCount(); + + for ( long nPos=0; nPos<nSkip; nPos++ ) + { + pAgg = pAgg->GetExistingChild(); + if (!pAgg) + return NULL; + } + + return pAgg; +} + +void ScDPDataMember::FillDataRow( const ScDPResultMember* pRefMember, + uno::Sequence<sheet::DataResult>& rSequence, + long& rCol, long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState ) const +{ + DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); + + if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::FillDataRow ??? + { + long nStartCol = rCol; + + const ScDPDataDimension* pDataChild = GetChildDimension(); + const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + + const ScDPLevel* pRefParentLevel = const_cast<ScDPResultMember*>(pRefMember)->GetParentLevel(); + + long nExtraSpace = 0; + if ( pRefParentLevel && pRefParentLevel->IsAddEmpty() ) + ++nExtraSpace; + + BOOL bTitleLine = FALSE; + if ( pRefParentLevel && pRefParentLevel->IsOutlineLayout() ) + bTitleLine = TRUE; + + BOOL bSubTotalInTitle = pRefMember->IsSubTotalInTitle( nMeasure ); + + // leave space for children even if the DataMember hasn't been initialized + // (pDataChild is null then, this happens when no values for it are in this row) + BOOL bHasChild = ( pRefChild != NULL ); + + if ( bHasChild ) + { + if ( bTitleLine ) // in tabular layout the title is on a separate column + ++rCol; // -> fill child dimension one column below + + if ( pDataChild ) + pDataChild->FillDataRow( pRefChild, rSequence, rCol, nMeasure, bIsSubTotalRow, rSubState ); + rCol += (USHORT)pRefMember->GetSize( nMeasure ); + + if ( bTitleLine ) // title column is included in GetSize, so the following + --rCol; // positions are calculated with the normal values + } + + long nUserSubStart; + long nUserSubCount = pRefMember->GetSubTotalCount(&nUserSubStart); + if ( nUserSubCount || !bHasChild ) + { + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + { + nUserSubCount = 1; + nUserSubStart = 0; + } + + ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + if (bHasChild) + { + rCol -= nSubSize * ( nUserSubCount - nUserSubStart ); // GetSize includes space for SubTotal + rCol -= nExtraSpace; // GetSize includes the empty line + } + + long nMoveSubTotal = 0; + if ( bSubTotalInTitle ) + { + nMoveSubTotal = rCol - nStartCol; // force to first (title) column + rCol = nStartCol; + } + + for (long nUserPos=nUserSubStart; nUserPos<nUserSubCount; nUserPos++) + { + if ( pChildDimension && nUserSubCount > 1 ) + { + const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; + aLocalSubState.nColSubTotalFunc = nUserPos; + aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + + DBG_ASSERT( rCol < rSequence.getLength(), "bumm" ); + sheet::DataResult& rRes = rSequence.getArray()[rCol]; + + if ( HasData( nMemberMeasure, aLocalSubState ) ) + { + if ( HasError( nMemberMeasure, aLocalSubState ) ) + { + rRes.Value = 0; + rRes.Flags |= sheet::DataResultFlags::ERROR; + } + else + { + rRes.Value = GetAggregate( nMemberMeasure, aLocalSubState ); + rRes.Flags |= sheet::DataResultFlags::HASDATA; + } + } + + if ( bHasChild || bIsSubTotalRow ) + rRes.Flags |= sheet::DataResultFlags::SUBTOTAL; + + rCol += 1; + } + } + + // add extra space again if subtracted from GetSize above, + // add to own size if no children + rCol += nExtraSpace; + + rCol += nMoveSubTotal; + } + } +} + +void ScDPDataMember::UpdateDataRow( const ScDPResultMember* pRefMember, + long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState ) +{ + DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); + + // Calculate must be called even if not visible (for use as reference value) + const ScDPDataDimension* pDataChild = GetChildDimension(); + const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + + // leave space for children even if the DataMember hasn't been initialized + // (pDataChild is null then, this happens when no values for it are in this row) + BOOL bHasChild = ( pRefChild != NULL ); + + // process subtotals even if not shown + long nUserSubCount = pRefMember->GetSubTotalCount(); + + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + nUserSubCount = 1; + + ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + if ( pChildDimension && nUserSubCount > 1 ) + { + const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; + aLocalSubState.nColSubTotalFunc = nUserPos; + aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + + // update data... + ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); + if (pAggData) + { + //! aLocalSubState? + ScSubTotalFunc eFunc = pResultData->GetMeasureFunction( nMemberMeasure ); + sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); + sal_Int32 eRefType = aReferenceValue.ReferenceType; + + // calculate the result first - for all members, regardless of reference value + pAggData->Calculate( eFunc, aLocalSubState ); + + if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) + { + // copy the result into auxiliary value, so differences can be + // calculated in any order + pAggData->SetAuxiliary( pAggData->GetResult() ); + } + // column/row percentage/index is now in UpdateRunningTotals, so it doesn't disturb sorting + } + } + } + + if ( bHasChild ) // child dimension must be processed last, so the row total is known + { + if ( pDataChild ) + pDataChild->UpdateDataRow( pRefChild, nMeasure, bIsSubTotalRow, rSubState ); + } +} + +void ScDPDataMember::SortMembers( ScDPResultMember* pRefMember ) +{ + DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); + + if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? + { + ScDPDataDimension* pDataChild = GetChildDimension(); + ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + if ( pRefChild && pDataChild ) + pDataChild->SortMembers( pRefChild ); // sorting is done at the dimension + } +} + +void ScDPDataMember::DoAutoShow( ScDPResultMember* pRefMember ) +{ + DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); + + if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension ??? + { + ScDPDataDimension* pDataChild = GetChildDimension(); + ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + if ( pRefChild && pDataChild ) + pDataChild->DoAutoShow( pRefChild ); // sorting is done at the dimension + } +} + +void ScDPDataMember::ResetResults() +{ + aAggregate.Reset(); + + ScDPDataDimension* pDataChild = GetChildDimension(); + if ( pDataChild ) + pDataChild->ResetResults(); +} + +void ScDPDataMember::UpdateRunningTotals( const ScDPResultMember* pRefMember, + long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, + ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) +{ + DBG_ASSERT( pRefMember == pResultMember || !pResultMember, "bla" ); + + if ( pRefMember->IsVisible() ) //! here or in ScDPDataDimension::UpdateRunningTotals ??? + { + const ScDPDataDimension* pDataChild = GetChildDimension(); + const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + + BOOL bIsRoot = ( pResultMember == NULL || pResultMember->GetParentLevel() == NULL ); + + // leave space for children even if the DataMember hasn't been initialized + // (pDataChild is null then, this happens when no values for it are in this row) + BOOL bHasChild = ( pRefChild != NULL ); + + long nUserSubCount = pRefMember->GetSubTotalCount(); + if ( nUserSubCount || !bHasChild ) + { + // Calculate at least automatic if no subtotals are selected, + // show only own values if there's no child dimension (innermost). + if ( !nUserSubCount || !bHasChild ) + nUserSubCount = 1; + + ScDPSubTotalState aLocalSubState(rSubState); // keep row state, modify column + + long nMemberMeasure = nMeasure; + long nSubSize = pResultData->GetCountForMeasure(nMeasure); + + for (long nUserPos=0; nUserPos<nUserSubCount; nUserPos++) // including hidden "automatic" + { + if ( pChildDimension && nUserSubCount > 1 ) + { + const ScDPLevel* pForceLevel = pResultMember ? pResultMember->GetParentLevel() : NULL; + aLocalSubState.nColSubTotalFunc = nUserPos; + aLocalSubState.eColForce = lcl_GetForceFunc( pForceLevel, nUserPos ); + } + + for ( long nSubCount=0; nSubCount<nSubSize; nSubCount++ ) + { + if ( nMeasure == SC_DPMEASURE_ALL ) + nMemberMeasure = nSubCount; + + // update data... + ScDPAggData* pAggData = GetAggData( nMemberMeasure, aLocalSubState ); + if (pAggData) + { + //! aLocalSubState? + sheet::DataPilotFieldReference aReferenceValue = pResultData->GetMeasureRefVal( nMemberMeasure ); + sal_Int32 eRefType = aReferenceValue.ReferenceType; + + if ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE ) + { + BOOL bRunningTotal = ( eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ); + BOOL bRelative = + ( aReferenceValue.ReferenceItemType != sheet::DataPilotFieldReferenceItemType::NAMED && !bRunningTotal ); + long nRelativeDir = bRelative ? + ( ( aReferenceValue.ReferenceItemType == sheet::DataPilotFieldReferenceItemType::PREVIOUS ) ? -1 : 1 ) : 0; + + const long* pColVisible = rRunning.GetColVisible(); + const long* pColIndexes = rRunning.GetColIndexes(); + const long* pRowVisible = rRunning.GetRowVisible(); + const long* pRowIndexes = rRunning.GetRowIndexes(); + + String aRefFieldName = aReferenceValue.ReferenceField; + + //! aLocalSubState? + USHORT nRefOrient = pResultData->GetMeasureRefOrient( nMemberMeasure ); + BOOL bRefDimInCol = ( nRefOrient == sheet::DataPilotFieldOrientation_COLUMN ); + BOOL bRefDimInRow = ( nRefOrient == sheet::DataPilotFieldOrientation_ROW ); + + const ScDPResultDimension* pSelectDim = NULL; + long nRowPos = 0; + long nColPos = 0; + + // + // find the reference field in column or row dimensions + // + + if ( bRefDimInRow ) // look in row dimensions + { + pSelectDim = rRunning.GetRowResRoot()->GetChildDimension(); + while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) + { + long nIndex = pRowIndexes[nRowPos]; + if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) + pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); + else + pSelectDim = NULL; + ++nRowPos; + } + // child dimension of innermost member? + if ( pSelectDim && pRowIndexes[nRowPos] < 0 ) + pSelectDim = NULL; + } + + if ( bRefDimInCol ) // look in column dimensions + { + pSelectDim = rRunning.GetColResRoot()->GetChildDimension(); + while ( pSelectDim && pSelectDim->GetName() != aRefFieldName ) + { + long nIndex = pColIndexes[nColPos]; + if ( nIndex >= 0 && nIndex < pSelectDim->GetMemberCount() ) + pSelectDim = pSelectDim->GetMember(nIndex)->GetChildDimension(); + else + pSelectDim = NULL; + ++nColPos; + } + // child dimension of innermost member? + if ( pSelectDim && pColIndexes[nColPos] < 0 ) + pSelectDim = NULL; + } + + BOOL bNoDetailsInRef = FALSE; + if ( pSelectDim && bRunningTotal ) + { + // Running totals: + // If details are hidden for this member in the reference dimension, + // don't show or sum up the value. Otherwise, for following members, + // the running totals of details and subtotals wouldn't match. + + long nMyIndex = bRefDimInCol ? pColIndexes[nColPos] : pRowIndexes[nRowPos]; + if ( nMyIndex >= 0 && nMyIndex < pSelectDim->GetMemberCount() ) + { + const ScDPResultMember* pMyRefMember = pSelectDim->GetMember(nMyIndex); + if ( pMyRefMember && pMyRefMember->HasHiddenDetails() ) + { + pSelectDim = NULL; // don't calculate + bNoDetailsInRef = TRUE; // show error, not empty + } + } + } + + if ( bRelative ) + { + // Difference/Percentage from previous/next: + // If details are hidden for this member in the innermost column/row + // dimension (the orientation of the reference dimension), show an + // error value. + // - If the no-details dimension is the reference dimension, its + // members will be skipped when finding the previous/next member, + // so there must be no results for its members. + // - If the no-details dimension is outside of the reference dimension, + // no calculation in the reference dimension is possible. + // - Otherwise, the error isn't strictly necessary, but shown for + // consistency. + + BOOL bInnerNoDetails = bRefDimInCol ? HasHiddenDetails() : + ( bRefDimInRow ? rRowParent.HasHiddenDetails() : TRUE ); + if ( bInnerNoDetails ) + { + pSelectDim = NULL; + bNoDetailsInRef = TRUE; // show error, not empty + } + } + + if ( !bRefDimInCol && !bRefDimInRow ) // invalid dimension specified + bNoDetailsInRef = TRUE; // pSelectDim is then already NULL + + // + // get the member for the reference item and do the calculation + // + + if ( bRunningTotal ) + { + // running total in (dimension) -> find first existing member + + if ( pSelectDim ) + { + ScDPDataMember* pSelectMember; + if ( bRefDimInCol ) + pSelectMember = ScDPResultDimension::GetColReferenceMember( NULL, NULL, + nColPos, rRunning ); + else + { + long nSkip = nRowPos + 1; // including the reference dimension + pSelectMember = pSelectDim->GetRowReferenceMember( NULL, NULL, + pRowIndexes+nSkip, pColIndexes ); + } + + if ( pSelectMember ) + { + // The running total is kept as the auxiliary value in + // the first available member for the reference dimension. + // Members are visited in final order, so each one's result + // can be used and then modified. + + ScDPAggData* pSelectData = pSelectMember-> + GetAggData( nMemberMeasure, aLocalSubState ); + if ( pSelectData ) + { + double fTotal = pSelectData->GetAuxiliary(); + fTotal += pAggData->GetResult(); + pSelectData->SetAuxiliary( fTotal ); + pAggData->SetResult( fTotal ); + pAggData->SetEmpty(FALSE); // always display + } + } + else + pAggData->SetError(); + } + else if (bNoDetailsInRef) + pAggData->SetError(); + else + pAggData->SetEmpty(TRUE); // empty (dim set to 0 above) + } + else + { + // difference/percentage -> find specified member + + if ( pSelectDim ) + { + String aRefItemName = aReferenceValue.ReferenceItemName; + ScDPRelativePos aRefItemPos( 0, nRelativeDir ); // nBasePos is modified later + + const String* pRefName = NULL; + const ScDPRelativePos* pRefPos = NULL; + if ( bRelative ) + pRefPos = &aRefItemPos; + else + pRefName = &aRefItemName; + + ScDPDataMember* pSelectMember; + if ( bRefDimInCol ) + { + aRefItemPos.nBasePos = pColVisible[nColPos]; // without sort order applied + pSelectMember = ScDPResultDimension::GetColReferenceMember( pRefPos, pRefName, + nColPos, rRunning ); + } + else + { + aRefItemPos.nBasePos = pRowVisible[nRowPos]; // without sort order applied + long nSkip = nRowPos + 1; // including the reference dimension + pSelectMember = pSelectDim->GetRowReferenceMember( pRefPos, pRefName, + pRowIndexes+nSkip, pColIndexes ); + } + + // difference or perc.difference is empty for the reference item itself + if ( pSelectMember == this && + eRefType != sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE ) + { + pAggData->SetEmpty(TRUE); + } + else if ( pSelectMember ) + { + const ScDPAggData* pOtherAggData = pSelectMember-> + GetConstAggData( nMemberMeasure, aLocalSubState ); + DBG_ASSERT( pOtherAggData, "no agg data" ); + if ( pOtherAggData ) + { + // Reference member may be visited before or after this one, + // so the auxiliary value is used for the original result. + + double fOtherResult = pOtherAggData->GetAuxiliary(); + double fThisResult = pAggData->GetResult(); + BOOL bError = FALSE; + switch ( eRefType ) + { + case sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE: + fThisResult = fThisResult - fOtherResult; + break; + case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE: + if ( fOtherResult == 0.0 ) + bError = TRUE; + else + fThisResult = fThisResult / fOtherResult; + break; + case sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE: + if ( fOtherResult == 0.0 ) + bError = TRUE; + else + fThisResult = ( fThisResult - fOtherResult ) / fOtherResult; + break; + default: + DBG_ERROR("invalid calculation type"); + } + if ( bError ) + { + pAggData->SetError(); + } + else + { + pAggData->SetResult(fThisResult); + pAggData->SetEmpty(FALSE); // always display + } + //! errors in data? + } + } + else if (bRelative && !bNoDetailsInRef) + pAggData->SetEmpty(TRUE); // empty + else + pAggData->SetError(); // error + } + else if (bNoDetailsInRef) + pAggData->SetError(); // error + else + pAggData->SetEmpty(TRUE); // empty + } + } + else if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::INDEX ) + { + // + // set total values when they are encountered (always before their use) + // + + ScDPAggData* pColTotalData = pRefMember->GetColTotal( nMemberMeasure ); + ScDPAggData* pRowTotalData = rTotals.GetRowTotal( nMemberMeasure ); + ScDPAggData* pGrandTotalData = rTotals.GetGrandTotal( nMemberMeasure ); + + double fTotalValue = pAggData->HasError() ? 0 : pAggData->GetResult(); + + if ( bIsRoot && rTotals.IsInColRoot() && pGrandTotalData ) + pGrandTotalData->SetAuxiliary( fTotalValue ); + + if ( bIsRoot && pRowTotalData ) + pRowTotalData->SetAuxiliary( fTotalValue ); + + if ( rTotals.IsInColRoot() && pColTotalData ) + pColTotalData->SetAuxiliary( fTotalValue ); + + // + // find relation to total values + // + + switch ( eRefType ) + { + case sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE: + case sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE: + case sheet::DataPilotFieldReferenceType::TOTAL_PERCENTAGE: + { + double nTotal; + if ( eRefType == sheet::DataPilotFieldReferenceType::ROW_PERCENTAGE ) + nTotal = pRowTotalData->GetAuxiliary(); + else if ( eRefType == sheet::DataPilotFieldReferenceType::COLUMN_PERCENTAGE ) + nTotal = pColTotalData->GetAuxiliary(); + else + nTotal = pGrandTotalData->GetAuxiliary(); + + if ( nTotal == 0.0 ) + pAggData->SetError(); + else + pAggData->SetResult( pAggData->GetResult() / nTotal ); + } + break; + case sheet::DataPilotFieldReferenceType::INDEX: + { + double nColTotal = pColTotalData->GetAuxiliary(); + double nRowTotal = pRowTotalData->GetAuxiliary(); + double nGrandTotal = pGrandTotalData->GetAuxiliary(); + if ( nRowTotal == 0.0 || nColTotal == 0.0 ) + pAggData->SetError(); + else + pAggData->SetResult( + ( pAggData->GetResult() * nGrandTotal ) / + ( nRowTotal * nColTotal ) ); + } + break; + } + } + } + } + } + } + + if ( bHasChild ) // child dimension must be processed last, so the row total is known + { + if ( pDataChild ) + pDataChild->UpdateRunningTotals( pRefChild, nMeasure, + bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); + } + } +} + +void ScDPDataMember::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const +{ + lcl_DumpRow( String::CreateFromAscii("ScDPDataMember"), GetName(), &aAggregate, pDoc, rPos ); + SCROW nStartRow = rPos.Row(); + + const ScDPDataDimension* pDataChild = GetChildDimension(); + const ScDPResultDimension* pRefChild = pRefMember->GetChildDimension(); + if ( pDataChild && pRefChild ) + pDataChild->DumpState( pRefChild, pDoc, rPos ); + + lcl_Indent( pDoc, nStartRow, rPos ); +} + +// ----------------------------------------------------------------------- + +// Helper class to select the members to include in +// ScDPResultDimension::InitFrom or LateInitFrom if groups are used + +class ScDPGroupCompare +{ +private: + const ScDPResultData* pResultData; + const ScDPInitState& rInitState; + long nDimSource; + BOOL bIncludeAll; + BOOL bIsBase; + long nGroupBase; + const ScDPItemData* pBaseData; + +public: + ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ); + ~ScDPGroupCompare() {} + + BOOL IsIncluded( const ScDPMember& rMember ) { return bIncludeAll || TestIncluded( rMember ); } + BOOL TestIncluded( const ScDPMember& rMember ); +}; + +ScDPGroupCompare::ScDPGroupCompare( const ScDPResultData* pData, const ScDPInitState& rState, long nDimension ) : + pResultData( pData ), + rInitState( rState ), + nDimSource( nDimension ), + pBaseData( NULL ) +{ + bIsBase = pResultData->IsBaseForGroup( nDimSource ); + nGroupBase = pResultData->GetGroupBase( nDimSource ); //! get together in one call? + if ( nGroupBase >= 0 ) + pBaseData = rInitState.GetNameForIndex( nGroupBase ); + + // if bIncludeAll is set, TestIncluded doesn't need to be called + bIncludeAll = !( bIsBase || nGroupBase >= 0 ); +} + +BOOL ScDPGroupCompare::TestIncluded( const ScDPMember& rMember ) +{ + BOOL bInclude = TRUE; + if ( pBaseData ) + { + ScDPItemData aMemberData; + rMember.FillItemData( aMemberData ); + bInclude = pResultData->IsInGroup( aMemberData, nDimSource, *pBaseData, nGroupBase ); + } + else if ( bIsBase ) + { + // need to check all previous groups + //! get array of groups (or indexes) before loop? + ScDPItemData aMemberData; + rMember.FillItemData( aMemberData ); + long nInitCount = rInitState.GetCount(); + const long* pInitSource = rInitState.GetSource(); + const ScDPItemData* pInitNames = rInitState.GetNames(); + for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) + if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nDimSource ) + { + bInclude = pResultData->IsInGroup( pInitNames[nInitPos], pInitSource[nInitPos], + aMemberData, nDimSource ); + } + } + else if ( nGroupBase >= 0 ) + { + // base isn't used in preceding fields + // -> look for other groups using the same base + + //! get array of groups (or indexes) before loop? + ScDPItemData aMemberData; + rMember.FillItemData( aMemberData ); + long nInitCount = rInitState.GetCount(); + const long* pInitSource = rInitState.GetSource(); + const ScDPItemData* pInitNames = rInitState.GetNames(); + for (long nInitPos=0; nInitPos<nInitCount && bInclude; nInitPos++) + if ( pResultData->GetGroupBase( pInitSource[nInitPos] ) == nGroupBase ) + { + // same base (hierarchy between the two groups is irrelevant) + bInclude = pResultData->HasCommonElement( pInitNames[nInitPos], pInitSource[nInitPos], + aMemberData, nDimSource ); + } + } + + return bInclude; +} + +// ----------------------------------------------------------------------- + +ScDPResultDimension::ScDPResultDimension( const ScDPResultData* pData ) : + pResultData( pData ), + bInitialized( FALSE ), + bIsDataLayout( FALSE ), + bSortByData( FALSE ), + bSortAscending( FALSE ), + nSortMeasure( 0 ), + bAutoShow( FALSE ), + bAutoTopItems( FALSE ), + nAutoMeasure( 0 ), + nAutoCount( 0 ) +{ +} + +ScDPResultDimension::~ScDPResultDimension() +{ + for( int i = maMemberArray.size () ; i-- > 0 ; ) + delete maMemberArray[i]; +} + +ScDPResultMember *ScDPResultDimension::FindMember( const ScDPItemData& rData ) const +{ + if( bIsDataLayout ) + return maMemberArray[0]; + + MemberHash::const_iterator aRes = maMemberHash.find( rData ); + if( aRes != maMemberHash.end()) { + if ( aRes->second->IsNamedItem( rData ) ) + return aRes->second; + DBG_ERROR("problem! hash result is not the same as IsNamedItem"); + } + + unsigned int i; + unsigned int nCount = maMemberArray.size(); + ScDPResultMember* pResultMember; + for( i = 0; i < nCount ; i++ ) + { + pResultMember = maMemberArray[i]; + if ( pResultMember->IsNamedItem( rData ) ) + return pResultMember; + } + return NULL; +} + +void ScDPResultDimension::InitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, + size_t nPos, ScDPInitState& rInitState ) +{ + if (nPos >= ppDim.size() || nPos >= ppLev.size()) + { + bInitialized = true; + return; + } + + ScDPDimension* pThisDim = ppDim[nPos]; + ScDPLevel* pThisLevel = ppLev[nPos]; + + if (!pThisDim || !pThisLevel) + { + bInitialized = true; + return; + } + + bIsDataLayout = pThisDim->getIsDataLayoutDimension(); // member + aDimensionName = pThisDim->getName(); // member + + // Check the autoshow setting. If it's enabled, store the settings. + const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); + if ( rAutoInfo.IsEnabled ) + { + bAutoShow = TRUE; + bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); + nAutoMeasure = pThisLevel->GetAutoMeasure(); + nAutoCount = rAutoInfo.ItemCount; + } + + // Check the sort info, and store the settings if appropriate. + const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); + if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) + { + bSortByData = TRUE; + bSortAscending = rSortInfo.IsAscending; + nSortMeasure = pThisLevel->GetSortMeasure(); + } + + // global order is used to initialize aMembers, so it doesn't have to be looked at later + const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); + + long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? + ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); + + // Now, go through all members and initialize them. + ScDPMembers* pMembers = pThisLevel->GetMembersObject(); + long nMembCount = pMembers->getCount(); + for ( long i=0; i<nMembCount; i++ ) + { + long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; + + ScDPMember* pMember = pMembers->getByIndex(nSorted); + if ( aCompare.IsIncluded( *pMember ) ) + { + ScDPResultMember* pNew = new ScDPResultMember( pResultData, pThisDim, + pThisLevel, pMember, FALSE ); + maMemberArray.push_back( pNew ); + + ScDPItemData aMemberData; + pMember->FillItemData( aMemberData ); + + // honour order of maMemberArray and only insert if it does not + // already exist + if ( maMemberHash.end() == maMemberHash.find( aMemberData ) ) + maMemberHash.insert( std::pair< const ScDPItemData, ScDPResultMember *>( aMemberData, pNew ) ); + + rInitState.AddMember( nDimSource, aMemberData ); + pNew->InitFrom( ppDim, ppLev, nPos+1, rInitState ); + rInitState.RemoveMember(); + } + } + bInitialized = TRUE; +} + +void ScDPResultDimension::LateInitFrom( const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLev, + const vector<ScDPItemData>& pItemData, size_t nPos, + ScDPInitState& rInitState ) +{ + if (nPos >= ppDim.size() || nPos >= ppLev.size() || nPos >= pItemData.size()) + return; + + ScDPDimension* pThisDim = ppDim[nPos]; + ScDPLevel* pThisLevel = ppLev[nPos]; + const ScDPItemData& rThisData = pItemData[nPos]; + + if (!pThisDim || !pThisLevel) + return; + + long nDimSource = pThisDim->GetDimension(); //! check GetSourceDim? + + if ( !bInitialized ) + { + // create all members at the first call (preserve order) + + bIsDataLayout = pThisDim->getIsDataLayoutDimension(); + aDimensionName = pThisDim->getName(); + + const sheet::DataPilotFieldAutoShowInfo& rAutoInfo = pThisLevel->GetAutoShow(); + if ( rAutoInfo.IsEnabled ) + { + bAutoShow = TRUE; + bAutoTopItems = ( rAutoInfo.ShowItemsMode == sheet::DataPilotFieldShowItemsMode::FROM_TOP ); + nAutoMeasure = pThisLevel->GetAutoMeasure(); + nAutoCount = rAutoInfo.ItemCount; + } + + const sheet::DataPilotFieldSortInfo& rSortInfo = pThisLevel->GetSortInfo(); + if ( rSortInfo.Mode == sheet::DataPilotFieldSortMode::DATA ) + { + bSortByData = TRUE; + bSortAscending = rSortInfo.IsAscending; + nSortMeasure = pThisLevel->GetSortMeasure(); + } + + // global order is used to initialize aMembers, so it doesn't have to be looked at later + const ScMemberSortOrder& rGlobalOrder = pThisLevel->GetGlobalOrder(); + + ScDPGroupCompare aCompare( pResultData, rInitState, nDimSource ); + + ScDPMembers* pMembers = pThisLevel->GetMembersObject(); + long nMembCount = pMembers->getCount(); + for ( long i=0; i<nMembCount; i++ ) + { + long nSorted = rGlobalOrder.empty() ? i : rGlobalOrder[i]; + + ScDPMember* pMember = pMembers->getByIndex(nSorted); + if ( aCompare.IsIncluded( *pMember ) ) + { + ScDPResultMember* pNew = new ScDPResultMember( pResultData, pThisDim, + pThisLevel, pMember, FALSE ); + maMemberArray.push_back( pNew ); + + ScDPItemData aMemberData; + pMember->FillItemData( aMemberData ); + + // honour order of maMemberArray and only insert if it does not + // already exist + if ( maMemberHash.end() == maMemberHash.find( aMemberData ) ) + maMemberHash.insert( std::pair< const ScDPItemData, ScDPResultMember *>( aMemberData, pNew ) ); + } + } + bInitialized = TRUE; // don't call again, even if no members were included + } + + // initialize only specific member (or all if "show empty" flag is set) + + BOOL bShowEmpty = pThisLevel->getShowEmpty(); + if ( bIsDataLayout || bShowEmpty ) + { + long nCount = maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + ScDPResultMember* pResultMember = maMemberArray[i]; + ScDPItemData aMemberData; + pResultMember->FillItemData( aMemberData ); + rInitState.AddMember( nDimSource, aMemberData ); + pResultMember->LateInitFrom( ppDim, ppLev, pItemData, nPos+1, rInitState ); + rInitState.RemoveMember(); + } + } + else + { + ScDPResultMember* pResultMember = FindMember( rThisData ); + if( NULL != pResultMember ) + { + ScDPItemData aMemberData; + pResultMember->FillItemData( aMemberData ); + rInitState.AddMember( nDimSource, aMemberData ); + pResultMember->LateInitFrom( ppDim, ppLev, pItemData, nPos+1, rInitState ); + rInitState.RemoveMember(); + } + } +} + +long ScDPResultDimension::GetSize(long nMeasure) const +{ + long nTotal = 0; + long nMemberCount = maMemberArray.size(); + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + // repeat first member... + nTotal = nMemberCount * maMemberArray[0]->GetSize(0); // all measures have equal size + } + else + { + // add all members + for (long nMem=0; nMem<nMemberCount; nMem++) + nTotal += maMemberArray[nMem]->GetSize(nMeasure); + } + return nTotal; +} + +bool ScDPResultDimension::IsValidEntry( const vector<ScDPItemData>& aMembers ) const +{ + if (aMembers.empty()) + return false; + + const ScDPResultMember* pMember = FindMember( aMembers[0] ); + if ( NULL != pMember ) + return pMember->IsValidEntry( aMembers ); + + DBG_ERROR("IsValidEntry: Member not found"); + return false; +} + +void ScDPResultDimension::ProcessData( const vector<ScDPItemData>& aMembers, + const ScDPResultDimension* pDataDim, + const vector<ScDPItemData>& aDataMembers, + const vector<ScDPValueData>& aValues ) const +{ + if (aMembers.empty()) + return; + + ScDPResultMember* pMember = FindMember( aMembers[0] ); + if ( NULL != pMember ) + { + vector<ScDPItemData> aChildMembers; + if (aMembers.size() > 1) + { + vector<ScDPItemData>::const_iterator itr = aMembers.begin(); + aChildMembers.assign(++itr, aMembers.end()); + } + pMember->ProcessData( aChildMembers, pDataDim, aDataMembers, aValues ); + return; + } + + DBG_ERROR("ProcessData: Member not found"); +} + +void ScDPResultDimension::FillMemberResults( uno::Sequence<sheet::MemberResult>* pSequences, + long nStart, long nMeasure ) +{ + long nPos = nStart; + long nCount = maMemberArray.size(); + + for (long i=0; i<nCount; i++) + { + long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; + + ScDPResultMember* pMember = maMemberArray[nSorted]; + // in data layout dimension, use first member with different measures/names + if ( bIsDataLayout ) + { + String aMbrName = pResultData->GetMeasureDimensionName( nSorted ); + String aMbrCapt = pResultData->GetMeasureString( nSorted, FALSE, SUBTOTAL_FUNC_NONE ); + maMemberArray[0]->FillMemberResults( pSequences, nPos, nSorted, FALSE, &aMbrName, &aMbrCapt ); + } + else if ( pMember->IsVisible() ) + pMember->FillMemberResults( pSequences, nPos, nMeasure, FALSE, NULL, NULL ); + // nPos is modified + } +} + +void ScDPResultDimension::FillDataResults( const ScDPResultMember* pRefMember, + uno::Sequence< uno::Sequence<sheet::DataResult> >& rSequence, + long nRow, long nMeasure ) const +{ + long nMemberRow = nRow; + long nMemberMeasure = nMeasure; + long nCount = maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; + + const ScDPResultMember* pMember; + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + pMember = maMemberArray[0]; + nMemberMeasure = nSorted; + } + else + pMember = maMemberArray[nSorted]; + + if ( pMember->IsVisible() ) + pMember->FillDataResults( pRefMember, rSequence, nMemberRow, nMemberMeasure ); + // nMemberRow is modified + } +} + +void ScDPResultDimension::UpdateDataResults( const ScDPResultMember* pRefMember, long nMeasure ) const +{ + long nMemberMeasure = nMeasure; + long nCount = maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + const ScDPResultMember* pMember; + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + pMember = maMemberArray[0]; + nMemberMeasure = i; + } + else + pMember = maMemberArray[i]; + + if ( pMember->IsVisible() ) + pMember->UpdateDataResults( pRefMember, nMemberMeasure ); + } +} + +void ScDPResultDimension::SortMembers( ScDPResultMember* pRefMember ) +{ + long nCount = maMemberArray.size(); + + if ( bSortByData ) + { + // sort members + + DBG_ASSERT( aMemberOrder.empty(), "sort twice?" ); + aMemberOrder.resize( nCount ); + for (long nPos=0; nPos<nCount; nPos++) + aMemberOrder[nPos] = nPos; + + ScDPRowMembersOrder aComp( *this, nSortMeasure, bSortAscending ); + ::std::sort( aMemberOrder.begin(), aMemberOrder.end(), aComp ); + } + + // handle children + + // for data layout, call only once - sorting measure is always taken from settings + long nLoopCount = bIsDataLayout ? 1 : nCount; + for (long i=0; i<nLoopCount; i++) + { + ScDPResultMember* pMember = maMemberArray[i]; + if ( pMember->IsVisible() ) + pMember->SortMembers( pRefMember ); + } +} + +void ScDPResultDimension::DoAutoShow( ScDPResultMember* pRefMember ) +{ + long nCount = maMemberArray.size(); + + // handle children first, before changing the visible state + + // for data layout, call only once - sorting measure is always taken from settings + long nLoopCount = bIsDataLayout ? 1 : nCount; + for (long i=0; i<nLoopCount; i++) + { + ScDPResultMember* pMember = maMemberArray[i]; + if ( pMember->IsVisible() ) + pMember->DoAutoShow( pRefMember ); + } + + if ( bAutoShow && nAutoCount > 0 && nAutoCount < nCount ) + { + // establish temporary order, hide remaining members + + ScMemberSortOrder aAutoOrder; + aAutoOrder.resize( nCount ); + long nPos; + for (nPos=0; nPos<nCount; nPos++) + aAutoOrder[nPos] = nPos; + + ScDPRowMembersOrder aComp( *this, nAutoMeasure, !bAutoTopItems ); + ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); + + // look for equal values to the last included one + + long nIncluded = nAutoCount; + const ScDPResultMember* pMember1 = maMemberArray[aAutoOrder[nIncluded - 1]]; + const ScDPDataMember* pDataMember1 = pMember1->IsVisible() ? pMember1->GetDataRoot() : NULL; + BOOL bContinue = TRUE; + while ( bContinue ) + { + bContinue = FALSE; + if ( nIncluded < nCount ) + { + const ScDPResultMember* pMember2 = maMemberArray[aAutoOrder[nIncluded]]; + const ScDPDataMember* pDataMember2 = pMember2->IsVisible() ? pMember2->GetDataRoot() : NULL; + + if ( lcl_IsEqual( pDataMember1, pDataMember2, nAutoMeasure ) ) + { + ++nIncluded; // include more members if values are equal + bContinue = TRUE; + } + } + } + + // hide the remaining members + + for (nPos = nIncluded; nPos < nCount; nPos++) + { + ScDPResultMember* pMember = maMemberArray[aAutoOrder[nPos]]; + pMember->SetAutoHidden(); + } + } +} + +void ScDPResultDimension::ResetResults() +{ + long nCount = maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + // sort order doesn't matter + ScDPResultMember* pMember = maMemberArray[bIsDataLayout ? 0 : i]; + pMember->ResetResults( FALSE ); + } +} + +long ScDPResultDimension::GetSortedIndex( long nUnsorted ) const +{ + return aMemberOrder.empty() ? nUnsorted : aMemberOrder[nUnsorted]; +} + +void ScDPResultDimension::UpdateRunningTotals( const ScDPResultMember* pRefMember, long nMeasure, + ScDPRunningTotalState& rRunning, ScDPRowTotals& rTotals ) const +{ + const ScDPResultMember* pMember; + long nMemberMeasure = nMeasure; + long nCount = maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + long nSorted = aMemberOrder.empty() ? i : aMemberOrder[i]; + + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + pMember = maMemberArray[0]; + nMemberMeasure = nSorted; + } + else + pMember = maMemberArray[nSorted]; + + if ( pMember->IsVisible() ) + { + if ( bIsDataLayout ) + rRunning.AddRowIndex( 0, 0 ); + else + rRunning.AddRowIndex( i, nSorted ); + pMember->UpdateRunningTotals( pRefMember, nMemberMeasure, rRunning, rTotals ); + rRunning.RemoveRowIndex(); + } + } +} + +ScDPDataMember* ScDPResultDimension::GetRowReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, + const long* pRowIndexes, const long* pColIndexes ) const +{ + // get named, previous/next, or first member of this dimension (first existing if pRelativePos and pName are NULL) + + DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); + + ScDPDataMember* pColMember = NULL; + + BOOL bFirstExisting = ( pRelativePos == NULL && pName == NULL ); + long nMemberCount = maMemberArray.size(); + long nMemberIndex = 0; // unsorted + long nDirection = 1; // forward if no relative position is used + if ( pRelativePos ) + { + nDirection = pRelativePos->nDirection; + nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below + + DBG_ASSERT( nDirection == 1 || nDirection == -1, "Direction must be 1 or -1" ); + } + else if ( pName ) + { + // search for named member + + const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; + + //! use ScDPItemData, as in ScDPDimension::IsValidPage? + while ( pRowMember && pRowMember->GetName() != *pName ) + { + ++nMemberIndex; + if ( nMemberIndex < nMemberCount ) + pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; + else + pRowMember = NULL; + } + } + + BOOL bContinue = TRUE; + while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nMemberCount ) + { + const ScDPResultMember* pRowMember = maMemberArray[GetSortedIndex(nMemberIndex)]; + + // get child members by given indexes + + const long* pNextRowIndex = pRowIndexes; + while ( *pNextRowIndex >= 0 && pRowMember ) + { + const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); + if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) + pRowMember = pRowChild->GetMember( *pNextRowIndex ); + else + pRowMember = NULL; + ++pNextRowIndex; + } + + if ( pRowMember && pRelativePos ) + { + // Skip the member if it has hidden details + // (because when looking for the details, it is skipped, too). + // Also skip if the member is invisible because it has no data, + // for consistent ordering. + if ( pRowMember->HasHiddenDetails() || !pRowMember->IsVisible() ) + pRowMember = NULL; + } + + if ( pRowMember ) + { + pColMember = pRowMember->GetDataRoot(); + + const long* pNextColIndex = pColIndexes; + while ( *pNextColIndex >= 0 && pColMember ) + { + const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); + if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) + pColMember = pColChild->GetMember( *pNextColIndex ); + else + pColMember = NULL; + ++pNextColIndex; + } + } + + // continue searching only if looking for first existing or relative position + bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); + nMemberIndex += nDirection; + } + + return pColMember; +} + +// static +ScDPDataMember* ScDPResultDimension::GetColReferenceMember( const ScDPRelativePos* pRelativePos, const String* pName, + long nRefDimPos, const ScDPRunningTotalState& rRunning ) +{ + DBG_ASSERT( pRelativePos == NULL || pName == NULL, "can't use position and name" ); + + const long* pColIndexes = rRunning.GetColIndexes(); + const long* pRowIndexes = rRunning.GetRowIndexes(); + + // get own row member using all indexes + + const ScDPResultMember* pRowMember = rRunning.GetRowResRoot(); + ScDPDataMember* pColMember = NULL; + + const long* pNextRowIndex = pRowIndexes; + while ( *pNextRowIndex >= 0 && pRowMember ) + { + const ScDPResultDimension* pRowChild = pRowMember->GetChildDimension(); + if ( pRowChild && *pNextRowIndex < pRowChild->GetMemberCount() ) + pRowMember = pRowChild->GetMember( *pNextRowIndex ); + else + pRowMember = NULL; + ++pNextRowIndex; + } + + // get column (data) members before the reference field + //! pass rRowParent from ScDPDataMember::UpdateRunningTotals instead + + if ( pRowMember ) + { + pColMember = pRowMember->GetDataRoot(); + + const long* pNextColIndex = pColIndexes; + long nColSkipped = 0; + while ( *pNextColIndex >= 0 && pColMember && nColSkipped < nRefDimPos ) + { + const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); + if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) + pColMember = pColChild->GetMember( *pNextColIndex ); + else + pColMember = NULL; + ++pNextColIndex; + ++nColSkipped; + } + } + + // get column member for the reference field + + if ( pColMember ) + { + const ScDPDataDimension* pReferenceDim = pColMember->GetChildDimension(); + if ( pReferenceDim ) + { + long nReferenceCount = pReferenceDim->GetMemberCount(); + + BOOL bFirstExisting = ( pRelativePos == NULL && pName == NULL ); + long nMemberIndex = 0; // unsorted + long nDirection = 1; // forward if no relative position is used + pColMember = NULL; // don't use parent dimension's member if none found + if ( pRelativePos ) + { + nDirection = pRelativePos->nDirection; + nMemberIndex = pRelativePos->nBasePos + nDirection; // bounds are handled below + } + else if ( pName ) + { + // search for named member + + pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); + + //! use ScDPItemData, as in ScDPDimension::IsValidPage? + while ( pColMember && pColMember->GetName() != *pName ) + { + ++nMemberIndex; + if ( nMemberIndex < nReferenceCount ) + pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); + else + pColMember = NULL; + } + } + + BOOL bContinue = TRUE; + while ( bContinue && nMemberIndex >= 0 && nMemberIndex < nReferenceCount ) + { + pColMember = pReferenceDim->GetMember( pReferenceDim->GetSortedIndex( nMemberIndex ) ); + + // get column members below the reference field + + const long* pNextColIndex = pColIndexes + nRefDimPos + 1; + while ( *pNextColIndex >= 0 && pColMember ) + { + const ScDPDataDimension* pColChild = pColMember->GetChildDimension(); + if ( pColChild && *pNextColIndex < pColChild->GetMemberCount() ) + pColMember = pColChild->GetMember( *pNextColIndex ); + else + pColMember = NULL; + ++pNextColIndex; + } + + if ( pColMember && pRelativePos ) + { + // Skip the member if it has hidden details + // (because when looking for the details, it is skipped, too). + // Also skip if the member is invisible because it has no data, + // for consistent ordering. + if ( pColMember->HasHiddenDetails() || !pColMember->IsVisible() ) + pColMember = NULL; + } + + // continue searching only if looking for first existing or relative position + bContinue = ( pColMember == NULL && ( bFirstExisting || pRelativePos ) ); + nMemberIndex += nDirection; + } + } + else + pColMember = NULL; + } + + return pColMember; +} + +void ScDPResultDimension::DumpState( const ScDPResultMember* pRefMember, ScDocument* pDoc, ScAddress& rPos ) const +{ + String aDimName = bIsDataLayout ? String::CreateFromAscii("(data layout)") : GetName(); + lcl_DumpRow( String::CreateFromAscii("ScDPResultDimension"), aDimName, NULL, pDoc, rPos ); + + SCROW nStartRow = rPos.Row(); + + long nCount = bIsDataLayout ? 1 : maMemberArray.size(); + for (long i=0; i<nCount; i++) + { + const ScDPResultMember* pMember = maMemberArray[i]; + pMember->DumpState( pRefMember, pDoc, rPos ); + } + + lcl_Indent( pDoc, nStartRow, rPos ); +} + +long ScDPResultDimension::GetMemberCount() const +{ + return maMemberArray.size(); +} + +const ScDPResultMember* ScDPResultDimension::GetMember(long n) const +{ + return maMemberArray[n]; +} +ScDPResultMember* ScDPResultDimension::GetMember(long n) +{ + return maMemberArray[n]; +} + +ScDPResultDimension* ScDPResultDimension::GetFirstChildDimension() const +{ + if ( maMemberArray.size() > 0 ) + return maMemberArray[0]->GetChildDimension(); + else + return NULL; +} + +void ScDPResultDimension::FillVisibilityData(ScDPResultVisibilityData& rData) const +{ + if (IsDataLayout()) + return; + + MemberArray::const_iterator itr = maMemberArray.begin(), itrEnd = maMemberArray.end(); + + for (;itr != itrEnd; ++itr) + { + ScDPResultMember* pMember = *itr; + if (pMember->IsValid()) + { + ScDPItemData aItem; + pMember->FillItemData(aItem); + rData.addVisibleMember(GetName(), aItem); + pMember->FillVisibilityData(rData); + } + } +} + +// ----------------------------------------------------------------------- + +ScDPDataDimension::ScDPDataDimension( const ScDPResultData* pData ) : + pResultData( pData ), + pResultDimension( NULL ), + bIsDataLayout( FALSE ) +{ +} + +ScDPDataDimension::~ScDPDataDimension() +{ +} + +void ScDPDataDimension::InitFrom( const ScDPResultDimension* pDim ) +{ + if (!pDim) + return; + + pResultDimension = pDim; + bIsDataLayout = pDim->IsDataLayout(); + + // Go through all result members under the given result dimension, and + // create a new data member instance for each result member. + long nCount = pDim->GetMemberCount(); + for (long i=0; i<nCount; i++) + { + const ScDPResultMember* pResMem = pDim->GetMember(i); + + ScDPDataMember* pNew = new ScDPDataMember( pResultData, pResMem ); + aMembers.Insert( pNew, aMembers.Count() ); + + if ( !pResultData->IsLateInit() ) + { + // with LateInit, pResMem hasn't necessarily been initialized yet, + // so InitFrom for the new result member is called from its ProcessData method + + const ScDPResultDimension* pChildDim = pResMem->GetChildDimension(); + if ( pChildDim ) + pNew->InitFrom( pChildDim ); + } + } +} + +void ScDPDataDimension::ProcessData( const vector<ScDPItemData>& aDataMembers, const vector<ScDPValueData>& aValues, + const ScDPSubTotalState& rSubState ) +{ + // the ScDPItemData array must contain enough entries for all dimensions - this isn't checked + + long nCount = aMembers.Count(); + for (long i=0; i<nCount; i++) + { + ScDPDataMember* pMember = aMembers[(USHORT)i]; + + // always first member for data layout dim + if ( bIsDataLayout || ( !aDataMembers.empty() && pMember->IsNamedItem(aDataMembers[0]) ) ) + { + vector<ScDPItemData> aChildDataMembers; + if (aDataMembers.size() > 1) + { + vector<ScDPItemData>::const_iterator itr = aDataMembers.begin(); + aChildDataMembers.assign(++itr, aDataMembers.end()); + } + pMember->ProcessData( aChildDataMembers, aValues, rSubState ); + return; + } + } + + DBG_ERROR("ProcessData: Member not found"); +} + +void ScDPDataDimension::FillDataRow( const ScDPResultDimension* pRefDim, + uno::Sequence<sheet::DataResult>& rSequence, + long nCol, long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState ) const +{ + DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); + DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); + + const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); + + long nMemberMeasure = nMeasure; + long nMemberCol = nCol; + long nCount = aMembers.Count(); + for (long i=0; i<nCount; i++) + { + long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; + + long nMemberPos = nSorted; + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + nMemberPos = 0; + nMemberMeasure = nSorted; + } + + const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); + if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::FillDataRow ??? + { + const ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos]; + pDataMember->FillDataRow( pRefMember, rSequence, nMemberCol, nMemberMeasure, bIsSubTotalRow, rSubState ); + // nMemberCol is modified + } + } +} + +void ScDPDataDimension::UpdateDataRow( const ScDPResultDimension* pRefDim, + long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState ) const +{ + DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); + DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); + + long nMemberMeasure = nMeasure; + long nCount = aMembers.Count(); + for (long i=0; i<nCount; i++) + { + long nMemberPos = i; + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + nMemberPos = 0; + nMemberMeasure = i; + } + + // Calculate must be called even if the member is not visible (for use as reference value) + const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); + ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos]; + pDataMember->UpdateDataRow( pRefMember, nMemberMeasure, bIsSubTotalRow, rSubState ); + } +} + +void ScDPDataDimension::SortMembers( ScDPResultDimension* pRefDim ) +{ + long nCount = aMembers.Count(); + + if ( pRefDim->IsSortByData() ) + { + // sort members + + ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); + DBG_ASSERT( rMemberOrder.empty(), "sort twice?" ); + rMemberOrder.resize( nCount ); + for (long nPos=0; nPos<nCount; nPos++) + rMemberOrder[nPos] = nPos; + + ScDPColMembersOrder aComp( *this, pRefDim->GetSortMeasure(), pRefDim->IsSortAscending() ); + ::std::sort( rMemberOrder.begin(), rMemberOrder.end(), aComp ); + } + + // handle children + + DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); + DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); + + // for data layout, call only once - sorting measure is always taken from settings + long nLoopCount = bIsDataLayout ? 1 : nCount; + for (long i=0; i<nLoopCount; i++) + { + ScDPResultMember* pRefMember = pRefDim->GetMember(i); + if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? + { + ScDPDataMember* pDataMember = aMembers[(USHORT)i]; + pDataMember->SortMembers( pRefMember ); + } + } +} + +void ScDPDataDimension::DoAutoShow( ScDPResultDimension* pRefDim ) +{ + long nCount = aMembers.Count(); + + // handle children first, before changing the visible state + + DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); + DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); + + // for data layout, call only once - sorting measure is always taken from settings + long nLoopCount = bIsDataLayout ? 1 : nCount; + for (long i=0; i<nLoopCount; i++) + { + ScDPResultMember* pRefMember = pRefDim->GetMember(i); + if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember ??? + { + ScDPDataMember* pDataMember = aMembers[(USHORT)i]; + pDataMember->DoAutoShow( pRefMember ); + } + } + + if ( pRefDim->IsAutoShow() && pRefDim->GetAutoCount() > 0 && pRefDim->GetAutoCount() < nCount ) + { + // establish temporary order, hide remaining members + + ScMemberSortOrder aAutoOrder; + aAutoOrder.resize( nCount ); + long nPos; + for (nPos=0; nPos<nCount; nPos++) + aAutoOrder[nPos] = nPos; + + ScDPColMembersOrder aComp( *this, pRefDim->GetAutoMeasure(), !pRefDim->IsAutoTopItems() ); + ::std::sort( aAutoOrder.begin(), aAutoOrder.end(), aComp ); + + // look for equal values to the last included one + + long nIncluded = pRefDim->GetAutoCount(); + ScDPDataMember* pDataMember1 = aMembers[(USHORT)aAutoOrder[nIncluded - 1]]; + if ( !pDataMember1->IsVisible() ) + pDataMember1 = NULL; + BOOL bContinue = TRUE; + while ( bContinue ) + { + bContinue = FALSE; + if ( nIncluded < nCount ) + { + ScDPDataMember* pDataMember2 = aMembers[(USHORT)aAutoOrder[nIncluded]]; + if ( !pDataMember2->IsVisible() ) + pDataMember2 = NULL; + + if ( lcl_IsEqual( pDataMember1, pDataMember2, pRefDim->GetAutoMeasure() ) ) + { + ++nIncluded; // include more members if values are equal + bContinue = TRUE; + } + } + } + + // hide the remaining members + + for (nPos = nIncluded; nPos < nCount; nPos++) + { + ScDPResultMember* pMember = pRefDim->GetMember(aAutoOrder[nPos]); + pMember->SetAutoHidden(); + } + } +} + +void ScDPDataDimension::ResetResults() +{ + long nCount = aMembers.Count(); + for (long i=0; i<nCount; i++) + { + // sort order doesn't matter + + long nMemberPos = bIsDataLayout ? 0 : i; + ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos]; + pDataMember->ResetResults(); + } +} + +long ScDPDataDimension::GetSortedIndex( long nUnsorted ) const +{ + if (!pResultDimension) + return nUnsorted; + + const ScMemberSortOrder& rMemberOrder = pResultDimension->GetMemberOrder(); + return rMemberOrder.empty() ? nUnsorted : rMemberOrder[nUnsorted]; +} + +void ScDPDataDimension::UpdateRunningTotals( const ScDPResultDimension* pRefDim, + long nMeasure, BOOL bIsSubTotalRow, + const ScDPSubTotalState& rSubState, ScDPRunningTotalState& rRunning, + ScDPRowTotals& rTotals, const ScDPResultMember& rRowParent ) const +{ + DBG_ASSERT( pRefDim && pRefDim->GetMemberCount() == aMembers.Count(), "dimensions don't match" ); + DBG_ASSERT( pRefDim == pResultDimension, "wrong dim" ); + + long nMemberMeasure = nMeasure; + long nCount = aMembers.Count(); + for (long i=0; i<nCount; i++) + { + const ScMemberSortOrder& rMemberOrder = pRefDim->GetMemberOrder(); + long nSorted = rMemberOrder.empty() ? i : rMemberOrder[i]; + + long nMemberPos = nSorted; + if (bIsDataLayout) + { + DBG_ASSERT(nMeasure == SC_DPMEASURE_ALL || pResultData->GetMeasureCount() == 1, + "DataLayout dimension twice?"); + nMemberPos = 0; + nMemberMeasure = nSorted; + } + + const ScDPResultMember* pRefMember = pRefDim->GetMember(nMemberPos); + if ( pRefMember->IsVisible() ) //! here or in ScDPDataMember::UpdateRunningTotals ??? + { + if ( bIsDataLayout ) + rRunning.AddColIndex( 0, 0 ); + else + rRunning.AddColIndex( i, nSorted ); + + ScDPDataMember* pDataMember = aMembers[(USHORT)nMemberPos]; + pDataMember->UpdateRunningTotals( pRefMember, nMemberMeasure, + bIsSubTotalRow, rSubState, rRunning, rTotals, rRowParent ); + + rRunning.RemoveColIndex(); + } + } +} + +void ScDPDataDimension::DumpState( const ScDPResultDimension* pRefDim, ScDocument* pDoc, ScAddress& rPos ) const +{ + String aDimName = String::CreateFromAscii( bIsDataLayout ? "(data layout)" : "(unknown)" ); + lcl_DumpRow( String::CreateFromAscii("ScDPDataDimension"), aDimName, NULL, pDoc, rPos ); + + SCROW nStartRow = rPos.Row(); + + long nCount = bIsDataLayout ? 1 : aMembers.Count(); + for (long i=0; i<nCount; i++) + { + const ScDPResultMember* pRefMember = pRefDim->GetMember(i); + const ScDPDataMember* pDataMember = aMembers[(USHORT)i]; + pDataMember->DumpState( pRefMember, pDoc, rPos ); + } + + lcl_Indent( pDoc, nStartRow, rPos ); +} + +long ScDPDataDimension::GetMemberCount() const +{ + return aMembers.Count(); +} + +ScDPDataMember* ScDPDataDimension::GetMember(long n) const +{ + return aMembers[(USHORT)n]; +} + +// ---------------------------------------------------------------------------- + +ScDPResultVisibilityData::ScDPResultVisibilityData( + ScSimpleSharedString& rSharedString, ScDPSource* pSource) : + mrSharedString(rSharedString), + mpSource(pSource) +{ +} + +ScDPResultVisibilityData::~ScDPResultVisibilityData() +{ +} + +void ScDPResultVisibilityData::addVisibleMember(const String& rDimName, const ScDPItemData& rMemberItem) +{ + DimMemberType::iterator itr = maDimensions.find(rDimName); + if (itr == maDimensions.end()) + { + pair<DimMemberType::iterator, bool> r = maDimensions.insert( + DimMemberType::value_type(rDimName, VisibleMemberType())); + + if (!r.second) + // insertion failed. + return; + + itr = r.first; + } + VisibleMemberType& rMem = itr->second; + VisibleMemberType::iterator itrMem = rMem.find(rMemberItem); + if (itrMem == rMem.end()) + rMem.insert(rMemberItem); +} + +void ScDPResultVisibilityData::fillFieldFilters(vector<ScDPCacheTable::Criterion>& rFilters) const +{ + typedef hash_map<String, long, ScStringHashCode> FieldNameMapType; + FieldNameMapType aFieldNames; + ScDPTableData* pData = mpSource->GetData(); + long nColumnCount = pData->GetColumnCount(); + for (long i = 0; i < nColumnCount; ++i) + { + aFieldNames.insert( + FieldNameMapType::value_type(pData->getDimensionName(i), i)); + } + + const ScDPDimensions* pDims = mpSource->GetDimensionsObject(); + for (DimMemberType::const_iterator itr = maDimensions.begin(), itrEnd = maDimensions.end(); + itr != itrEnd; ++itr) + { + const String& rDimName = itr->first; + ScDPCacheTable::Criterion aCri; + FieldNameMapType::const_iterator itrField = aFieldNames.find(rDimName); + if (itrField == aFieldNames.end()) + // This should never happen! + continue; + + long nDimIndex = itrField->second; + aCri.mnFieldIndex = static_cast<sal_Int32>(nDimIndex); + aCri.mpFilter.reset(new ScDPCacheTable::GroupFilter(mrSharedString)); + ScDPCacheTable::GroupFilter* pGrpFilter = + static_cast<ScDPCacheTable::GroupFilter*>(aCri.mpFilter.get()); + + const VisibleMemberType& rMem = itr->second; + for (VisibleMemberType::const_iterator itrMem = rMem.begin(), itrMemEnd = rMem.end(); + itrMem != itrMemEnd; ++itrMem) + { + const ScDPItemData& rMemItem = *itrMem; + pGrpFilter->addMatchItem(rMemItem.aString, rMemItem.fValue, rMemItem.bHasValue); + } + + ScDPDimension* pDim = pDims->getByIndex(nDimIndex); + ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)-> + GetLevelsObject()->getByIndex(0)->GetMembersObject(); + if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(pMembers->getCount())) + rFilters.push_back(aCri); + } +} + +size_t ScDPResultVisibilityData::MemberHash::operator() (const ScDPItemData& r) const +{ + if (r.bHasValue) + return static_cast<size_t>(::rtl::math::approxFloor(r.fValue)); + else + return rtl_ustr_hashCode_WithLength(r.aString.GetBuffer(), r.aString.Len()); +} diff --git a/sc/source/core/data/dptabsrc.cxx b/sc/source/core/data/dptabsrc.cxx new file mode 100644 index 000000000000..3e9a7586fd1a --- /dev/null +++ b/sc/source/core/data/dptabsrc.cxx @@ -0,0 +1,2725 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: dptabsrc.cxx,v $ + * $Revision: 1.27 $ + * + * 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 <algorithm> +#include <vector> +#include <set> +#include <hash_map> +#include <hash_set> + +#include <tools/debug.hxx> +#include <rtl/math.hxx> +#include <svtools/itemprop.hxx> +#include <svtools/intitem.hxx> + +#include "scitems.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "cell.hxx" + +#include "dptabsrc.hxx" +#include "dptabres.hxx" +#include "dptabdat.hxx" +#include "global.hxx" +#include "collect.hxx" +#include "datauno.hxx" // ScDataUnoConversion +#include "unoguard.hxx" +#include "miscuno.hxx" +#include "unonames.hxx" + +#include <com/sun/star/beans/PropertyAttribute.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotFieldOrientation.hpp> +#include <com/sun/star/sheet/DataPilotFieldFilter.hpp> +#include <com/sun/star/sheet/DataPilotFieldReferenceType.hpp> +#include <com/sun/star/sheet/DataPilotFieldSortMode.hpp> +#include <com/sun/star/sheet/DataPilotFieldAutoShowInfo.hpp> +#include <com/sun/star/table/CellAddress.hpp> + +#include <unotools/collatorwrapper.hxx> +#include <unotools/calendarwrapper.hxx> +#include <com/sun/star/i18n/CalendarDisplayIndex.hpp> + +using namespace com::sun::star; +using ::std::vector; +using ::std::set; +using ::std::hash_map; +using ::std::hash_set; +using ::com::sun::star::uno::Reference; +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::uno::Any; +using ::com::sun::star::sheet::DataPilotFieldAutoShowInfo; + +// ----------------------------------------------------------------------- + +#define SC_MINCOUNT_LIMIT 1000000 + +// ----------------------------------------------------------------------- + +SC_SIMPLE_SERVICE_INFO( ScDPSource, "ScDPSource", "com.sun.star.sheet.DataPilotSource" ) +SC_SIMPLE_SERVICE_INFO( ScDPDimensions, "ScDPDimensions", "com.sun.star.sheet.DataPilotSourceDimensions" ) +SC_SIMPLE_SERVICE_INFO( ScDPDimension, "ScDPDimension", "com.sun.star.sheet.DataPilotSourceDimension" ) +SC_SIMPLE_SERVICE_INFO( ScDPHierarchies, "ScDPHierarchies", "com.sun.star.sheet.DataPilotSourceHierarcies" ) +SC_SIMPLE_SERVICE_INFO( ScDPHierarchy, "ScDPHierarchy", "com.sun.star.sheet.DataPilotSourceHierarcy" ) +SC_SIMPLE_SERVICE_INFO( ScDPLevels, "ScDPLevels", "com.sun.star.sheet.DataPilotSourceLevels" ) +SC_SIMPLE_SERVICE_INFO( ScDPLevel, "ScDPLevel", "com.sun.star.sheet.DataPilotSourceLevel" ) +SC_SIMPLE_SERVICE_INFO( ScDPMembers, "ScDPMembers", "com.sun.star.sheet.DataPilotSourceMembers" ) +SC_SIMPLE_SERVICE_INFO( ScDPMember, "ScDPMember", "com.sun.star.sheet.DataPilotSourceMember" ) + +// ----------------------------------------------------------------------- + +// property maps for PropertySetInfo +// DataDescription / NumberFormat are internal + +// ----------------------------------------------------------------------- + +//! move to a header? +BOOL lcl_GetBoolFromAny( const uno::Any& aAny ) +{ + if ( aAny.getValueTypeClass() == uno::TypeClass_BOOLEAN ) + return *(sal_Bool*)aAny.getValue(); + return FALSE; +} + +void lcl_SetBoolInAny( uno::Any& rAny, BOOL bValue ) +{ + rAny.setValue( &bValue, getBooleanCppuType() ); +} + +// ----------------------------------------------------------------------- + +ScDPSource::ScDPSource( ScDPTableData* pD ) : + pData( pD ), + pDimensions( NULL ), + nColDimCount( 0 ), + nRowDimCount( 0 ), + nDataDimCount( 0 ), + nPageDimCount( 0 ), + bColumnGrand( TRUE ), // default is true + bRowGrand( TRUE ), + bIgnoreEmptyRows( FALSE ), + bRepeatIfEmpty( FALSE ), + nDupCount( 0 ), + pResData( NULL ), + pColResRoot( NULL ), + pRowResRoot( NULL ), + pColResults( NULL ), + pRowResults( NULL ), + bResultOverflow( FALSE ) +{ + pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty ); +} + +ScDPSource::~ScDPSource() +{ + delete pData; // ScDPTableData is not ref-counted + + if (pDimensions) + pDimensions->release(); // ref-counted + + //! free lists + + delete[] pColResults; + delete[] pRowResults; + + delete pColResRoot; + delete pRowResRoot; + delete pResData; +} + +USHORT ScDPSource::GetOrientation(long nColumn) +{ + long i; + for (i=0; i<nColDimCount; i++) + if (nColDims[i] == nColumn) + return sheet::DataPilotFieldOrientation_COLUMN; + for (i=0; i<nRowDimCount; i++) + if (nRowDims[i] == nColumn) + return sheet::DataPilotFieldOrientation_ROW; + for (i=0; i<nDataDimCount; i++) + if (nDataDims[i] == nColumn) + return sheet::DataPilotFieldOrientation_DATA; + for (i=0; i<nPageDimCount; i++) + if (nPageDims[i] == nColumn) + return sheet::DataPilotFieldOrientation_PAGE; + return sheet::DataPilotFieldOrientation_HIDDEN; +} + +long ScDPSource::GetDataDimensionCount() +{ + return nDataDimCount; +} + +String ScDPSource::GetDataDimName( long nIndex ) +{ + String aRet; + if ( nIndex >= 0 && nIndex < nDataDimCount ) + { + long nDimIndex = nDataDims[nIndex]; + ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex); + if (pDim) + aRet = String( pDim->getName() ); + } + return aRet; +} + +long ScDPSource::GetPosition(long nColumn) +{ + long i; + for (i=0; i<nColDimCount; i++) + if (nColDims[i] == nColumn) + return i; + for (i=0; i<nRowDimCount; i++) + if (nRowDims[i] == nColumn) + return i; + for (i=0; i<nDataDimCount; i++) + if (nDataDims[i] == nColumn) + return i; + for (i=0; i<nPageDimCount; i++) + if (nPageDims[i] == nColumn) + return i; + return 0; +} + +BOOL lcl_TestSubTotal( BOOL& rAllowed, long nColumn, long* pArray, long nCount, ScDPSource* pSource ) +{ + for (long i=0; i<nCount; i++) + if (pArray[i] == nColumn) + { + // no subtotals for data layout dim, no matter where + if ( pSource->IsDataLayoutDimension(nColumn) ) + rAllowed = FALSE; + else + { + // no subtotals if no other dim but data layout follows + long nNextIndex = i+1; + if ( nNextIndex < nCount && pSource->IsDataLayoutDimension(pArray[nNextIndex]) ) + ++nNextIndex; + if ( nNextIndex >= nCount ) + rAllowed = FALSE; + } + + return TRUE; // found + } + return FALSE; +} + +BOOL ScDPSource::SubTotalAllowed(long nColumn) +{ + //! cache this at ScDPResultData + BOOL bAllowed = TRUE; + if ( lcl_TestSubTotal( bAllowed, nColumn, nColDims, nColDimCount, this ) ) + return bAllowed; + if ( lcl_TestSubTotal( bAllowed, nColumn, nRowDims, nRowDimCount, this ) ) + return bAllowed; + return bAllowed; +} + +void lcl_RemoveDim( long nRemove, long* pDims, long& rCount ) +{ + for (long i=0; i<rCount; i++) + if ( pDims[i] == nRemove ) + { + for (long j=i; j+1<rCount; j++) + pDims[j] = pDims[j+1]; + --rCount; + return; + } +} + +void ScDPSource::SetOrientation(long nColumn, USHORT nNew) +{ + //! change to no-op if new orientation is equal to old? + + // remove from old list + lcl_RemoveDim( nColumn, nColDims, nColDimCount ); + lcl_RemoveDim( nColumn, nRowDims, nRowDimCount ); + lcl_RemoveDim( nColumn, nDataDims, nDataDimCount ); + lcl_RemoveDim( nColumn, nPageDims, nPageDimCount ); + + // add to new list + switch (nNew) + { + case sheet::DataPilotFieldOrientation_COLUMN: + nColDims[nColDimCount++] = nColumn; + break; + case sheet::DataPilotFieldOrientation_ROW: + nRowDims[nRowDimCount++] = nColumn; + break; + case sheet::DataPilotFieldOrientation_DATA: + nDataDims[nDataDimCount++] = nColumn; + break; + case sheet::DataPilotFieldOrientation_PAGE: + nPageDims[nPageDimCount++] = nColumn; + break; + case sheet::DataPilotFieldOrientation_HIDDEN: + /* Do not assert HIDDEN as it occurs e.g. while using the + csss.XDataPilotTables.createDataPilotDescriptor() function. */ + break; + default: + DBG_ERROR( "ScDPSource::SetOrientation: unexpected orientation" ); + break; + } +} + +BOOL ScDPSource::IsDataLayoutDimension(long nDim) +{ + return nDim == pData->GetColumnCount(); +} + +USHORT ScDPSource::GetDataLayoutOrientation() +{ + return GetOrientation(pData->GetColumnCount()); +} + +BOOL ScDPSource::IsDateDimension(long nDim) +{ + return pData->IsDateDimension(nDim); +} + +ScDPDimensions* ScDPSource::GetDimensionsObject() +{ + if (!pDimensions) + { + pDimensions = new ScDPDimensions(this); + pDimensions->acquire(); // ref-counted + } + return pDimensions; +} + +uno::Reference<container::XNameAccess> SAL_CALL ScDPSource::getDimensions() throw(uno::RuntimeException) +{ + return GetDimensionsObject(); +} + +void ScDPSource::SetDupCount( long nNew ) +{ + nDupCount = nNew; +} + +ScDPDimension* ScDPSource::AddDuplicated(long /* nSource */, const String& rNewName) +{ + DBG_ASSERT( pDimensions, "AddDuplicated without dimensions?" ); + + // re-use + + long nOldDimCount = pDimensions->getCount(); + for (long i=0; i<nOldDimCount; i++) + { + ScDPDimension* pDim = pDimensions->getByIndex(i); + if (pDim && String(pDim->getName()) == rNewName) + { + //! test if pDim is a duplicate of source + return pDim; + } + } + + SetDupCount( nDupCount + 1 ); + pDimensions->CountChanged(); // uses nDupCount + + return pDimensions->getByIndex( pDimensions->getCount() - 1 ); +} + +long ScDPSource::GetSourceDim(long nDim) +{ + // original source dimension or data layout dimension? + if ( nDim <= pData->GetColumnCount() ) + return nDim; + + if ( nDim < pDimensions->getCount() ) + { + ScDPDimension* pDimObj = pDimensions->getByIndex( nDim ); + if ( pDimObj ) + { + long nSource = pDimObj->GetSourceDim(); + if ( nSource >= 0 ) + return nSource; + } + } + + DBG_ERROR("GetSourceDim: wrong dim"); + return nDim; +} + +uno::Sequence< uno::Sequence<sheet::DataResult> > SAL_CALL ScDPSource::getResults() + throw(uno::RuntimeException) +{ + CreateRes_Impl(); // create pColResRoot and pRowResRoot + + if ( bResultOverflow ) // set in CreateRes_Impl + { + // no results available + throw uno::RuntimeException(); + } + + long nColCount = pColResRoot->GetSize(pResData->GetColStartMeasure()); + long nRowCount = pRowResRoot->GetSize(pResData->GetRowStartMeasure()); + + // allocate full sequence + //! leave out empty rows??? + + uno::Sequence< uno::Sequence<sheet::DataResult> > aSeq( nRowCount ); + uno::Sequence<sheet::DataResult>* pRowAry = aSeq.getArray(); + for (long nRow = 0; nRow < nRowCount; nRow++) + { + uno::Sequence<sheet::DataResult> aColSeq( nColCount ); + // use default values of DataResult + pRowAry[nRow] = aColSeq; + } + + long nSeqRow = 0; + pRowResRoot->FillDataResults( pColResRoot, aSeq, nSeqRow, pResData->GetRowStartMeasure() ); + + return aSeq; +} + +void SAL_CALL ScDPSource::refresh() throw(uno::RuntimeException) +{ + disposeData(); +} + +void SAL_CALL ScDPSource::addRefreshListener( const uno::Reference<util::XRefreshListener >& ) + throw(uno::RuntimeException) +{ + DBG_ERROR("not implemented"); //! exception? +} + +void SAL_CALL ScDPSource::removeRefreshListener( const uno::Reference<util::XRefreshListener >& ) + throw(uno::RuntimeException) +{ + DBG_ERROR("not implemented"); //! exception? +} + +Sequence< Sequence<Any> > SAL_CALL ScDPSource::getDrillDownData(const Sequence<sheet::DataPilotFieldFilter>& aFilters) + throw (uno::RuntimeException) +{ + long nColumnCount = GetData()->GetColumnCount(); + ScSimpleSharedString& rSharedString = GetData()->GetSharedString(); + + typedef hash_map<String, long, ScStringHashCode> FieldNameMapType; + FieldNameMapType aFieldNames; + for (long i = 0; i < nColumnCount; ++i) + { + aFieldNames.insert( + FieldNameMapType::value_type(GetData()->getDimensionName(i), i)); + } + + // collect ScDPItemData for each filtered column + vector<ScDPCacheTable::Criterion> aFilterCriteria; + sal_Int32 nFilterCount = aFilters.getLength(); + for (sal_Int32 i = 0; i < nFilterCount; ++i) + { + const sheet::DataPilotFieldFilter& rFilter = aFilters[i]; + String aFieldName( rFilter.FieldName ); + for (long nCol = 0; nCol < nColumnCount; ++nCol) + { + if ( aFieldName == pData->getDimensionName(nCol) ) + { + ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nCol ); + ScDPMembers* pMembers = pDim->GetHierarchiesObject()->getByIndex(0)-> + GetLevelsObject()->getByIndex(0)->GetMembersObject(); + sal_Int32 nIndex = pMembers->GetIndexFromName( rFilter.MatchValue ); + if ( nIndex >= 0 ) + { + ScDPItemData aItem; + pMembers->getByIndex(nIndex)->FillItemData( aItem ); + aFilterCriteria.push_back( ScDPCacheTable::Criterion() ); + sal_Int32 nMatchStrId = rSharedString.getStringId(aItem.aString); + aFilterCriteria.back().mnFieldIndex = nCol; + aFilterCriteria.back().mpFilter.reset( + new ScDPCacheTable::SingleFilter(rSharedString, nMatchStrId, aItem.fValue, aItem.bHasValue) ); + } + } + } + } + + // Take into account the visibilities of field members. + ScDPResultVisibilityData aResVisData(rSharedString, this); + pRowResRoot->FillVisibilityData(aResVisData); + pColResRoot->FillVisibilityData(aResVisData); + aResVisData.fillFieldFilters(aFilterCriteria); + + Sequence< Sequence<Any> > aTabData; + hash_set<sal_Int32> aCatDims; + GetCategoryDimensionIndices(aCatDims); + pData->GetDrillDownData(aFilterCriteria, aCatDims, aTabData); + return aTabData; +} + +String ScDPSource::getDataDescription() +{ + CreateRes_Impl(); // create pResData + + String aRet; + if ( pResData->GetMeasureCount() == 1 ) + aRet = pResData->GetMeasureString( 0, TRUE, SUBTOTAL_FUNC_NONE ); + + // empty for more than one measure + + return aRet; +} + +BOOL ScDPSource::getColumnGrand() const +{ + return bColumnGrand; +} + +void ScDPSource::setColumnGrand(BOOL bSet) +{ + bColumnGrand = bSet; +} + +BOOL ScDPSource::getRowGrand() const +{ + return bRowGrand; +} + +void ScDPSource::setRowGrand(BOOL bSet) +{ + bRowGrand = bSet; +} + +BOOL ScDPSource::getIgnoreEmptyRows() const +{ + return bIgnoreEmptyRows; +} + +void ScDPSource::setIgnoreEmptyRows(BOOL bSet) +{ + bIgnoreEmptyRows = bSet; + pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty ); +} + +BOOL ScDPSource::getRepeatIfEmpty() const +{ + return bRepeatIfEmpty; +} + +void ScDPSource::setRepeatIfEmpty(BOOL bSet) +{ + bRepeatIfEmpty = bSet; + pData->SetEmptyFlags( bIgnoreEmptyRows, bRepeatIfEmpty ); +} + +void ScDPSource::validate() //! ??? +{ + CreateRes_Impl(); +} + +void ScDPSource::disposeData() +{ + if ( pResData ) + { + // reset all data... + + DELETEZ(pColResRoot); + DELETEZ(pRowResRoot); + DELETEZ(pResData); + delete[] pColResults; + delete[] pRowResults; + pColResults = NULL; + pRowResults = NULL; + aColLevelList.Clear(); + aRowLevelList.Clear(); + } + + if ( pDimensions ) + { + pDimensions->release(); // ref-counted + pDimensions = NULL; // settings have to be applied (from SaveData) again! + } + SetDupCount( 0 ); + + //! Test ???? + nColDimCount = nRowDimCount = nDataDimCount = nPageDimCount = 0; + + pData->DisposeData(); // cached entries etc. + bResultOverflow = FALSE; +} + +long lcl_CountMinMembers(const vector<ScDPDimension*>& ppDim, const vector<ScDPLevel*>& ppLevel, long nLevels ) +{ + // Calculate the product of the member count for those consecutive levels that + // have the "show all" flag, one following level, and the data layout dimension. + + long nTotal = 1; + long nDataCount = 1; + BOOL bWasShowAll = TRUE; + long nPos = nLevels; + while ( nPos > 0 ) + { + --nPos; + + if ( nPos+1 < nLevels && ppDim[nPos] == ppDim[nPos+1] ) + { + DBG_ERROR("lcl_CountMinMembers: multiple levels from one dimension not implemented"); + return 0; + } + + BOOL bDo = FALSE; + if ( ppDim[nPos]->getIsDataLayoutDimension() ) + { + // data layout dim doesn't interfere with "show all" flags + nDataCount = ppLevel[nPos]->GetMembersObject()->getCount(); + if ( nDataCount == 0 ) + nDataCount = 1; + } + else if ( bWasShowAll ) // "show all" set for all following levels? + { + bDo = TRUE; + if ( !ppLevel[nPos]->getShowEmpty() ) + { + // this level is counted, following ones are not + bWasShowAll = FALSE; + } + } + if ( bDo ) + { + long nThisCount = ppLevel[nPos]->GetMembersObject()->getMinMembers(); + if ( nThisCount == 0 ) + { + nTotal = 1; // empty level -> start counting from here + //! start with visible elements in this level? + } + else + { + if ( nTotal >= LONG_MAX / nThisCount ) + return LONG_MAX; // overflow + nTotal *= nThisCount; + } + } + } + + // always include data layout dim, even after restarting + if ( nTotal >= LONG_MAX / nDataCount ) + return LONG_MAX; // overflow + nTotal *= nDataCount; + + return nTotal; +} + +long lcl_GetIndexFromName( const rtl::OUString rName, const uno::Sequence<rtl::OUString>& rElements ) +{ + long nCount = rElements.getLength(); + const rtl::OUString* pArray = rElements.getConstArray(); + for (long nPos=0; nPos<nCount; nPos++) + if (pArray[nPos] == rName) + return nPos; + + return -1; // not found +} + +void ScDPSource::FillCalcInfo(bool bIsRow, ScDPTableData::CalcInfo& rInfo, bool &rHasAutoShow) +{ + long* nDims = bIsRow ? nRowDims : nColDims; + long nDimCount = bIsRow ? nRowDimCount : nColDimCount; + + for (long i = 0; i < nDimCount; ++i) + { + ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nDims[i] ); + long nHierarchy = pDim->getUsedHierarchy(); + if ( nHierarchy >= pDim->GetHierarchiesObject()->getCount() ) + nHierarchy = 0; + ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject(); + long nCount = pLevels->getCount(); + + //! Test + if ( pDim->getIsDataLayoutDimension() && nDataDimCount < 2 ) + nCount = 0; + //! Test + + for (long j = 0; j < nCount; ++j) + { + ScDPLevel* pLevel = pLevels->getByIndex(j); + pLevel->EvaluateSortOrder(); + + // no layout flags for column fields, only for row fields + pLevel->SetEnableLayout( bIsRow ); + + if ( pLevel->GetAutoShow().IsEnabled ) + rHasAutoShow = TRUE; + + if (bIsRow) + { + rInfo.aRowLevelDims.push_back(nDims[i]); + rInfo.aRowDims.push_back(pDim); + rInfo.aRowLevels.push_back(pLevel); + } + else + { + rInfo.aColLevelDims.push_back(nDims[i]); + rInfo.aColDims.push_back(pDim); + rInfo.aColLevels.push_back(pLevel); + } + + pLevel->GetMembersObject(); // initialize for groups + } + } +} + +void ScDPSource::GetCategoryDimensionIndices(hash_set<sal_Int32>& rCatDims) +{ + hash_set<sal_Int32> aCatDims; + for (long i = 0; i < nColDimCount; ++i) + { + sal_Int32 nDim = static_cast<sal_Int32>(nColDims[i]); + if (!IsDataLayoutDimension(nDim)) + aCatDims.insert(nDim); + } + + for (long i = 0; i < nRowDimCount; ++i) + { + sal_Int32 nDim = static_cast<sal_Int32>(nRowDims[i]); + if (!IsDataLayoutDimension(nDim)) + aCatDims.insert(nDim); + } + + for (long i = 0; i < nPageDimCount; ++i) + { + sal_Int32 nDim = static_cast<sal_Int32>(nPageDims[i]); + if (!IsDataLayoutDimension(nDim)) + aCatDims.insert(nDim); + } + + rCatDims.swap(aCatDims); +} + +void ScDPSource::FilterCacheTableByPageDimensions() +{ + ScSimpleSharedString& rSharedString = GetData()->GetSharedString(); + + // filter table by page dimensions. + vector<ScDPCacheTable::Criterion> aCriteria; + for (long i = 0; i < nPageDimCount; ++i) + { + ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nPageDims[i]); + long nField = pDim->GetDimension(); + + ScDPMembers* pMems = pDim->GetHierarchiesObject()->getByIndex(0)-> + GetLevelsObject()->getByIndex(0)->GetMembersObject(); + + long nMemCount = pMems->getCount(); + ScDPCacheTable::Criterion aFilter; + aFilter.mnFieldIndex = static_cast<sal_Int32>(nField); + aFilter.mpFilter.reset(new ScDPCacheTable::GroupFilter(rSharedString)); + ScDPCacheTable::GroupFilter* pGrpFilter = + static_cast<ScDPCacheTable::GroupFilter*>(aFilter.mpFilter.get()); + for (long j = 0; j < nMemCount; ++j) + { + ScDPMember* pMem = pMems->getByIndex(j); + if (pMem->getIsVisible()) + { + ScDPItemData aData; + pMem->FillItemData(aData); + pGrpFilter->addMatchItem(aData.aString, aData.fValue, aData.bHasValue); + } + } + if (pGrpFilter->getMatchItemCount() < static_cast<size_t>(nMemCount)) + // there is at least one invisible item. Add this filter criterion to the mix. + aCriteria.push_back(aFilter); + + if (!pDim || !pDim->HasSelectedPage()) + continue; + + const ScDPItemData& rData = pDim->GetSelectedData(); + aCriteria.push_back(ScDPCacheTable::Criterion()); + ScDPCacheTable::Criterion& r = aCriteria.back(); + r.mnFieldIndex = static_cast<sal_Int32>(nField); + sal_Int32 nStrId = rSharedString.getStringId(rData.aString); + r.mpFilter.reset( + new ScDPCacheTable::SingleFilter(rSharedString, nStrId, rData.fValue, rData.bHasValue)); + } + if (!aCriteria.empty()) + { + hash_set<sal_Int32> aCatDims; + GetCategoryDimensionIndices(aCatDims); + pData->FilterCacheTable(aCriteria, aCatDims); + } +} + +void ScDPSource::CreateRes_Impl() +{ + if ( !pResData ) + { + USHORT nDataOrient = GetDataLayoutOrientation(); + if ( nDataDimCount > 1 && ( nDataOrient != sheet::DataPilotFieldOrientation_COLUMN && + nDataOrient != sheet::DataPilotFieldOrientation_ROW ) ) + { + // if more than one data dimension, data layout orientation must be set + SetOrientation( pData->GetColumnCount(), sheet::DataPilotFieldOrientation_ROW ); + nDataOrient = sheet::DataPilotFieldOrientation_ROW; + } + + // TODO: Aggreate pDataNames, pDataRefValues, nDataRefOrient, and + // eDataFunctions into a structure and use vector instead of static + // or pointer arrays. + String* pDataNames = NULL; + sheet::DataPilotFieldReference* pDataRefValues = NULL; + ScSubTotalFunc eDataFunctions[SC_DAPI_MAXFIELDS]; + USHORT nDataRefOrient[SC_DAPI_MAXFIELDS]; + if (nDataDimCount) + { + pDataNames = new String[nDataDimCount]; + pDataRefValues = new sheet::DataPilotFieldReference[nDataDimCount]; + } + + ScDPTableData::CalcInfo aInfo; + + + // LateInit (initialize only those rows/children that are used) can be used unless + // any data dimension needs reference values from column/row dimensions + BOOL bLateInit = TRUE; + + // Go through all data dimensions (i.e. fields) and build their meta data + // so that they can be passed on to ScDPResultData instance later. + // TODO: aggregate all of data dimension info into a structure. + long i; + for (i=0; i<nDataDimCount; i++) + { + // Get function for each data field. + long nDimIndex = nDataDims[i]; + ScDPDimension* pDim = GetDimensionsObject()->getByIndex(nDimIndex); + sheet::GeneralFunction eUser = (sheet::GeneralFunction)pDim->getFunction(); + if (eUser == sheet::GeneralFunction_AUTO) + { + //! test for numeric data + eUser = sheet::GeneralFunction_SUM; + } + + // Map UNO's enum to internal enum ScSubTotalFunc. + eDataFunctions[i] = ScDataUnoConversion::GeneralToSubTotal( eUser ); + + // Get reference field/item information. + pDataRefValues[i] = pDim->GetReferenceValue(); + nDataRefOrient[i] = sheet::DataPilotFieldOrientation_HIDDEN; // default if not used + sal_Int32 eRefType = pDataRefValues[i].ReferenceType; + if ( eRefType == sheet::DataPilotFieldReferenceType::ITEM_DIFFERENCE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE || + eRefType == sheet::DataPilotFieldReferenceType::ITEM_PERCENTAGE_DIFFERENCE || + eRefType == sheet::DataPilotFieldReferenceType::RUNNING_TOTAL ) + { + long nColumn = lcl_GetIndexFromName( pDataRefValues[i].ReferenceField, + GetDimensionsObject()->getElementNames() ); + if ( nColumn >= 0 ) + { + nDataRefOrient[i] = GetOrientation( nColumn ); + // need fully initialized results to find reference values + // (both in column or row dimensions), so updated values or + // differences to 0 can be displayed even for empty results. + bLateInit = FALSE; + } + } + + pDataNames[i] = String( pDim->getName() ); //! label? + + // asterisk is added to duplicated dimension names by ScDPSaveData::WriteToSource + //! modify user visible strings as in ScDPResultData::GetMeasureString instead! + + pDataNames[i].EraseTrailingChars('*'); + + //! if the name is overridden by user, a flag must be set + //! so the user defined name replaces the function string and field name. + + //! the complete name (function and field) must be stored at the dimension + + long nSource = ((ScDPDimension*)pDim)->GetSourceDim(); + if (nSource >= 0) + aInfo.aDataSrcCols.push_back(nSource); + else + aInfo.aDataSrcCols.push_back(nDimIndex); + } + + pResData = new ScDPResultData( this ); + pResData->SetMeasureData( nDataDimCount, eDataFunctions, pDataRefValues, nDataRefOrient, pDataNames ); + pResData->SetDataLayoutOrientation(nDataOrient); + pResData->SetLateInit( bLateInit ); + + delete[] pDataNames; + delete[] pDataRefValues; + + bool bHasAutoShow = false; + + ScDPInitState aInitState; + + // Page field selections restrict the members shown in related fields + // (both in column and row fields). aInitState is filled with the page + // field selections, they are kept across the data iterator loop. + + for (i=0; i<nPageDimCount; i++) + { + ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nPageDims[i] ); + if ( pDim->HasSelectedPage() ) + aInitState.AddMember( nPageDims[i], pDim->GetSelectedData() ); + } + + pColResRoot = new ScDPResultMember( pResData, NULL, NULL, NULL, bColumnGrand ); + pRowResRoot = new ScDPResultMember( pResData, NULL, NULL, NULL, bRowGrand ); + + FillCalcInfo(false, aInfo, bHasAutoShow); + long nColLevelCount = aInfo.aColLevels.size(); + + pColResRoot->InitFrom( aInfo.aColDims, aInfo.aColLevels, 0, aInitState ); + pColResRoot->SetHasElements(); + + FillCalcInfo(true, aInfo, bHasAutoShow); + long nRowLevelCount = aInfo.aRowLevels.size(); + + if ( nRowLevelCount > 0 ) + { + // disable layout flags for the innermost row field (level) + aInfo.aRowLevels[nRowLevelCount-1]->SetEnableLayout( FALSE ); + } + + pRowResRoot->InitFrom( aInfo.aRowDims, aInfo.aRowLevels, 0, aInitState ); + pRowResRoot->SetHasElements(); + + // initialize members object also for all page dimensions (needed for numeric groups) + for (i=0; i<nPageDimCount; i++) + { + ScDPDimension* pDim = GetDimensionsObject()->getByIndex( nPageDims[i] ); + long nHierarchy = pDim->getUsedHierarchy(); + if ( nHierarchy >= pDim->GetHierarchiesObject()->getCount() ) + nHierarchy = 0; + + ScDPLevels* pLevels = pDim->GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject(); + long nCount = pLevels->getCount(); + for (long j=0; j<nCount; j++) + pLevels->getByIndex(j)->GetMembersObject(); // initialize for groups + } + + // pre-check: calculate minimum number of result columns / rows from + // levels that have the "show all" flag set + + long nMinColMembers = lcl_CountMinMembers( aInfo.aColDims, aInfo.aColLevels, nColLevelCount ); + long nMinRowMembers = lcl_CountMinMembers( aInfo.aRowDims, aInfo.aRowLevels, nRowLevelCount ); + + if ( nMinColMembers > SC_MINCOUNT_LIMIT || nMinRowMembers > SC_MINCOUNT_LIMIT ) + { + // resulting table is too big -> abort before calculating + // (this relies on late init, so no members are allocated in InitFrom above) + + bResultOverflow = TRUE; + } + else + { + FilterCacheTableByPageDimensions(); + + aInfo.aPageDims.reserve(nPageDimCount); + for (i = 0; i < nPageDimCount; ++i) + aInfo.aPageDims.push_back(nPageDims[i]); + + aInfo.pInitState = &aInitState; + aInfo.pColRoot = pColResRoot; + aInfo.pRowRoot = pRowResRoot; + pData->CalcResults(aInfo, false); + + // ---------------------------------------------------------------- + // With all data processed, calculate the final results: + + // UpdateDataResults calculates all original results from the collected values, + // and stores them as reference values if needed. + pRowResRoot->UpdateDataResults( pColResRoot, pResData->GetRowStartMeasure() ); + + if ( bHasAutoShow ) // do the double calculation only if AutoShow is used + { + // Find the desired members and set bAutoHidden flag for the others + pRowResRoot->DoAutoShow( pColResRoot ); + + // Reset all results to empty, so they can be built again with data for the + // desired members only. + pColResRoot->ResetResults( TRUE ); + pRowResRoot->ResetResults( TRUE ); + pData->CalcResults(aInfo, true); + + // Call UpdateDataResults again, with the new (limited) values. + pRowResRoot->UpdateDataResults( pColResRoot, pResData->GetRowStartMeasure() ); + } + + // SortMembers does the sorting by a result dimension, using the orginal results, + // but not running totals etc. + pRowResRoot->SortMembers( pColResRoot ); + + // UpdateRunningTotals calculates running totals along column/row dimensions, + // differences from other members (named or relative), and column/row percentages + // or index values. + // Running totals and relative differences need to be done using the sorted values. + // Column/row percentages and index values must be done after sorting, because the + // results may no longer be in the right order (row total for percentage of row is + // always 1). + ScDPRunningTotalState aRunning( pColResRoot, pRowResRoot ); + ScDPRowTotals aTotals; + pRowResRoot->UpdateRunningTotals( pColResRoot, pResData->GetRowStartMeasure(), aRunning, aTotals ); + + // ---------------------------------------------------------------- + } + } +} + +//UNUSED2009-05 void ScDPSource::DumpState( ScDocument* pDoc, const ScAddress& rPos ) +//UNUSED2009-05 { +//UNUSED2009-05 CreateRes_Impl(); +//UNUSED2009-05 +//UNUSED2009-05 ScAddress aDocPos( rPos ); +//UNUSED2009-05 +//UNUSED2009-05 if (pColResRoot->GetChildDimension()) +//UNUSED2009-05 pColResRoot->GetChildDimension()->DumpState( NULL, pDoc, aDocPos ); +//UNUSED2009-05 pRowResRoot->DumpState( pColResRoot, pDoc, aDocPos ); +//UNUSED2009-05 } + +void ScDPSource::FillLevelList( USHORT nOrientation, List& rList ) +{ + rList.Clear(); + + long nDimCount = 0; + long* pDimIndex = NULL; + switch (nOrientation) + { + case sheet::DataPilotFieldOrientation_COLUMN: + pDimIndex = nColDims; + nDimCount = nColDimCount; + break; + case sheet::DataPilotFieldOrientation_ROW: + pDimIndex = nRowDims; + nDimCount = nRowDimCount; + break; + case sheet::DataPilotFieldOrientation_DATA: + pDimIndex = nDataDims; + nDimCount = nDataDimCount; + break; + case sheet::DataPilotFieldOrientation_PAGE: + pDimIndex = nPageDims; + nDimCount = nPageDimCount; + break; + default: + DBG_ERROR( "ScDPSource::FillLevelList: unexpected orientation" ); + break; + } + if (!pDimIndex) + { + DBG_ERROR("invalid orientation"); + return; + } + + ScDPDimensions* pDims = GetDimensionsObject(); + for (long nDim=0; nDim<nDimCount; nDim++) + { + ScDPDimension* pDim = pDims->getByIndex(pDimIndex[nDim]); + DBG_ASSERT( pDim->getOrientation() == nOrientation, "orientations are wrong" ); + + ScDPHierarchies* pHiers = pDim->GetHierarchiesObject(); + long nHierarchy = pDim->getUsedHierarchy(); + if ( nHierarchy >= pHiers->getCount() ) + nHierarchy = 0; + ScDPHierarchy* pHier = pHiers->getByIndex(nHierarchy); + ScDPLevels* pLevels = pHier->GetLevelsObject(); + long nLevCount = pLevels->getCount(); + for (long nLev=0; nLev<nLevCount; nLev++) + { + ScDPLevel* pLevel = pLevels->getByIndex(nLev); + rList.Insert( pLevel, LIST_APPEND ); + } + } +} + +void ScDPSource::FillMemberResults() +{ + if ( !pColResults && !pRowResults ) + { + CreateRes_Impl(); + + if ( bResultOverflow ) // set in CreateRes_Impl + { + // no results available -> abort (leave empty) + // exception is thrown in ScDPSource::getResults + return; + } + + FillLevelList( sheet::DataPilotFieldOrientation_COLUMN, aColLevelList ); + long nColLevelCount = aColLevelList.Count(); + if (nColLevelCount) + { + long nColDimSize = pColResRoot->GetSize(pResData->GetColStartMeasure()); + pColResults = new uno::Sequence<sheet::MemberResult>[nColLevelCount]; + for (long i=0; i<nColLevelCount; i++) + pColResults[i].realloc(nColDimSize); + + // ScDPResultDimension* pColResDim = pColResRoot->GetChildDimension(); + // pColResDim->FillMemberResults( pColResults, 0, pResData->GetColStartMeasure() ); + long nPos = 0; + pColResRoot->FillMemberResults( pColResults, nPos, pResData->GetColStartMeasure(), + TRUE, NULL, NULL ); + } + + FillLevelList( sheet::DataPilotFieldOrientation_ROW, aRowLevelList ); + long nRowLevelCount = aRowLevelList.Count(); + if (nRowLevelCount) + { + long nRowDimSize = pRowResRoot->GetSize(pResData->GetRowStartMeasure()); + pRowResults = new uno::Sequence<sheet::MemberResult>[nRowLevelCount]; + for (long i=0; i<nRowLevelCount; i++) + pRowResults[i].realloc(nRowDimSize); + + // ScDPResultDimension* pRowResDim = pRowResRoot->GetChildDimension(); + // pRowResDim->FillMemberResults( pRowResults, 0, pResData->GetRowStartMeasure() ); + long nPos = 0; + pRowResRoot->FillMemberResults( pRowResults, nPos, pResData->GetRowStartMeasure(), + TRUE, NULL, NULL ); + } + } +} + +const uno::Sequence<sheet::MemberResult>* ScDPSource::GetMemberResults( ScDPLevel* pLevel ) +{ + FillMemberResults(); + + long i; + long nColCount = aColLevelList.Count(); + for (i=0; i<nColCount; i++) + { + ScDPLevel* pColLevel = (ScDPLevel*)aColLevelList.GetObject(i); + if ( pColLevel == pLevel ) + return pColResults+i; + } + long nRowCount = aRowLevelList.Count(); + for (i=0; i<nRowCount; i++) + { + ScDPLevel* pRowLevel = (ScDPLevel*)aRowLevelList.GetObject(i); + if ( pRowLevel == pLevel ) + return pRowResults+i; + } + return NULL; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPSource::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ScUnoGuard aGuard; + + static SfxItemPropertyMapEntry aDPSourceMap_Impl[] = + { + {MAP_CHAR_LEN(SC_UNO_COLGRAND), 0, &getBooleanCppuType(), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_DATADESC), 0, &getCppuType((rtl::OUString*)0), beans::PropertyAttribute::READONLY, 0 }, + {MAP_CHAR_LEN(SC_UNO_IGNOREEM), 0, &getBooleanCppuType(), 0, 0 }, // for sheet data only + {MAP_CHAR_LEN(SC_UNO_REPEATIF), 0, &getBooleanCppuType(), 0, 0 }, // for sheet data only + {MAP_CHAR_LEN(SC_UNO_ROWGRAND), 0, &getBooleanCppuType(), 0, 0 }, + {0,0,0,0,0,0} + }; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( aDPSourceMap_Impl ); + return aRef; +} + +void SAL_CALL ScDPSource::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_COLGRAND ) ) + setColumnGrand( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_ROWGRAND ) ) + setRowGrand( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_IGNOREEM ) ) + setIgnoreEmptyRows( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_REPEATIF ) ) + setRepeatIfEmpty( lcl_GetBoolFromAny( aValue ) ); + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } +} + +uno::Any SAL_CALL ScDPSource::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + uno::Any aRet; + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_COLGRAND ) ) + lcl_SetBoolInAny( aRet, getColumnGrand() ); + else if ( aNameStr.EqualsAscii( SC_UNO_ROWGRAND ) ) + lcl_SetBoolInAny( aRet, getRowGrand() ); + else if ( aNameStr.EqualsAscii( SC_UNO_IGNOREEM ) ) + lcl_SetBoolInAny( aRet, getIgnoreEmptyRows() ); + else if ( aNameStr.EqualsAscii( SC_UNO_REPEATIF ) ) + lcl_SetBoolInAny( aRet, getRepeatIfEmpty() ); + else if ( aNameStr.EqualsAscii( SC_UNO_DATADESC ) ) // read-only + aRet <<= rtl::OUString( getDataDescription() ); + else if ( aNameStr.EqualsAscii( SC_UNO_ROWFIELDCOUNT ) ) // read-only + aRet <<= static_cast<sal_Int32>(nRowDimCount); + else if ( aNameStr.EqualsAscii( SC_UNO_COLUMNFIELDCOUNT ) ) // read-only + aRet <<= static_cast<sal_Int32>(nColDimCount); + else if ( aNameStr.EqualsAscii( SC_UNO_DATAFIELDCOUNT ) ) // read-only + aRet <<= static_cast<sal_Int32>(nDataDimCount); + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPSource ) + +// ----------------------------------------------------------------------- + +ScDPDimensions::ScDPDimensions( ScDPSource* pSrc ) : + pSource( pSrc ), + ppDims( NULL ) +{ + //! hold pSource + + // include data layout dimension and duplicated dimensions + nDimCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount(); +} + +ScDPDimensions::~ScDPDimensions() +{ + //! release pSource + + if (ppDims) + { + for (long i=0; i<nDimCount; i++) + if ( ppDims[i] ) + ppDims[i]->release(); // ref-counted + delete[] ppDims; + } +} + +void ScDPDimensions::CountChanged() +{ + // include data layout dimension and duplicated dimensions + long nNewCount = pSource->GetData()->GetColumnCount() + 1 + pSource->GetDupCount(); + if ( ppDims ) + { + long i; + long nCopy = Min( nNewCount, nDimCount ); + ScDPDimension** ppNew = new ScDPDimension*[nNewCount]; + + for (i=0; i<nCopy; i++) // copy existing dims + ppNew[i] = ppDims[i]; + for (i=nCopy; i<nNewCount; i++) // clear additional pointers + ppNew[i] = NULL; + for (i=nCopy; i<nDimCount; i++) // delete old dims if count is decreased + if ( ppDims[i] ) + ppDims[i]->release(); // ref-counted + + delete[] ppDims; + ppDims = ppNew; + } + nDimCount = nNewCount; +} + +// very simple XNameAccess implementation using getCount/getByIndex + +uno::Any SAL_CALL ScDPDimensions::getByName( const rtl::OUString& aName ) + throw(container::NoSuchElementException, + lang::WrappedTargetException, uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + { + uno::Reference<container::XNamed> xNamed = getByIndex(i); + uno::Any aRet; + aRet <<= xNamed; + return aRet; + } + + throw container::NoSuchElementException(); +// return uno::Any(); +} + +uno::Sequence<rtl::OUString> SAL_CALL ScDPDimensions::getElementNames() throw(uno::RuntimeException) +{ + long nCount = getCount(); + uno::Sequence<rtl::OUString> aSeq(nCount); + rtl::OUString* pArr = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArr[i] = getByIndex(i)->getName(); + return aSeq; +} + +sal_Bool SAL_CALL ScDPDimensions::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + return TRUE; + return FALSE; +} + +uno::Type SAL_CALL ScDPDimensions::getElementType() throw(uno::RuntimeException) +{ + return getCppuType((uno::Reference<container::XNamed>*)0); +} + +sal_Bool SAL_CALL ScDPDimensions::hasElements() throw(uno::RuntimeException) +{ + return ( getCount() > 0 ); +} + +// end of XNameAccess implementation + +long ScDPDimensions::getCount() const +{ + // in tabular data, every column of source data is a dimension + + return nDimCount; +} + +ScDPDimension* ScDPDimensions::getByIndex(long nIndex) const +{ + if ( nIndex >= 0 && nIndex < nDimCount ) + { + if ( !ppDims ) + { + ((ScDPDimensions*)this)->ppDims = new ScDPDimension*[nDimCount]; + for (long i=0; i<nDimCount; i++) + ppDims[i] = NULL; + } + if ( !ppDims[nIndex] ) + { + ppDims[nIndex] = new ScDPDimension( pSource, nIndex ); + ppDims[nIndex]->acquire(); // ref-counted + } + + return ppDims[nIndex]; + } + + return NULL; //! exception? +} + +// ----------------------------------------------------------------------- + +ScDPDimension::ScDPDimension( ScDPSource* pSrc, long nD ) : + pSource( pSrc ), + nDim( nD ), + pHierarchies( NULL ), + nUsedHier( 0 ), + nFunction( SUBTOTAL_FUNC_SUM ), // sum is default + nSourceDim( -1 ), + bHasSelectedPage( FALSE ), + pSelectedData( NULL ) +{ + //! hold pSource +} + +ScDPDimension::~ScDPDimension() +{ + //! release pSource + + if ( pHierarchies ) + pHierarchies->release(); // ref-counted + + delete pSelectedData; +} + +ScDPHierarchies* ScDPDimension::GetHierarchiesObject() +{ + if (!pHierarchies) + { + pHierarchies = new ScDPHierarchies( pSource, nDim ); + pHierarchies->acquire(); // ref-counted + } + return pHierarchies; +} + +uno::Reference<container::XNameAccess> SAL_CALL ScDPDimension::getHierarchies() + throw(uno::RuntimeException) +{ + return GetHierarchiesObject(); +} + +::rtl::OUString SAL_CALL ScDPDimension::getName() throw(uno::RuntimeException) +{ + if (aName.Len()) + return aName; + else + return pSource->GetData()->getDimensionName( nDim ); +} + +void SAL_CALL ScDPDimension::setName( const ::rtl::OUString& rNewName ) throw(uno::RuntimeException) +{ + // used after cloning + aName = String( rNewName ); +} + +USHORT ScDPDimension::getOrientation() const +{ + return pSource->GetOrientation( nDim ); +} + +void ScDPDimension::setOrientation(USHORT nNew) +{ + pSource->SetOrientation( nDim, nNew ); +} + +long ScDPDimension::getPosition() const +{ + return pSource->GetPosition( nDim ); +} + +void ScDPDimension::setPosition(long /* nNew */) +{ + //! ... +} + +BOOL ScDPDimension::getIsDataLayoutDimension() const +{ + return pSource->GetData()->getIsDataLayoutDimension( nDim ); +} + +USHORT ScDPDimension::getFunction() const +{ + return nFunction; +} + +void ScDPDimension::setFunction(USHORT nNew) +{ + nFunction = nNew; +} + +long ScDPDimension::getUsedHierarchy() const +{ + return nUsedHier; +} + +void ScDPDimension::setUsedHierarchy(long /* nNew */) +{ + // #i52547# don't use the incomplete date hierarchy implementation - ignore the call + // nUsedHier = nNew; +} + +ScDPDimension* ScDPDimension::CreateCloneObject() +{ + DBG_ASSERT( nSourceDim < 0, "recursive duplicate - not implemented" ); + + //! set new name here, or temporary name ??? + String aNewName = aName; + + ScDPDimension* pNew = pSource->AddDuplicated( nDim, aNewName ); + + pNew->aName = aNewName; //! here or in source? + pNew->nSourceDim = nDim; //! recursive? + + return pNew; +} + +uno::Reference<util::XCloneable> SAL_CALL ScDPDimension::createClone() throw(uno::RuntimeException) +{ + return CreateCloneObject(); +} + +BOOL ScDPDimension::isDuplicated() const +{ + return (nSourceDim >= 0); +} + +const sheet::DataPilotFieldReference& ScDPDimension::GetReferenceValue() const +{ + return aReferenceValue; +} + +const ScDPItemData& ScDPDimension::GetSelectedData() +{ + if ( !pSelectedData ) + { + // find the named member to initialize pSelectedData from it, with name and value + + long nLevel = 0; // same as in ScDPObject::FillPageList + + long nHierarchy = getUsedHierarchy(); + if ( nHierarchy >= GetHierarchiesObject()->getCount() ) + nHierarchy = 0; + ScDPLevels* pLevels = GetHierarchiesObject()->getByIndex(nHierarchy)->GetLevelsObject(); + long nLevCount = pLevels->getCount(); + if ( nLevel < nLevCount ) + { + ScDPMembers* pMembers = pLevels->getByIndex(nLevel)->GetMembersObject(); + + //! merge with ScDPMembers::getByName + long nCount = pMembers->getCount(); + for (long i=0; i<nCount && !pSelectedData; i++) + { + ScDPMember* pMember = pMembers->getByIndex(i); + if ( pMember->GetNameStr() == aSelectedPage ) + { + pSelectedData = new ScDPItemData(); + pMember->FillItemData( *pSelectedData ); + } + } + } + + if ( !pSelectedData ) + pSelectedData = new ScDPItemData( aSelectedPage, 0.0, FALSE ); // default - name only + } + + return *pSelectedData; +} + +//UNUSED2009-05 BOOL ScDPDimension::IsValidPage( const ScDPItemData& rData ) +//UNUSED2009-05 { +//UNUSED2009-05 if ( bHasSelectedPage ) +//UNUSED2009-05 return rData.IsCaseInsEqual( GetSelectedData() ); +//UNUSED2009-05 +//UNUSED2009-05 return TRUE; // no selection -> all data +//UNUSED2009-05 } + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPDimension::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ScUnoGuard aGuard; + + static SfxItemPropertyMapEntry aDPDimensionMap_Impl[] = + { + {MAP_CHAR_LEN(SC_UNO_FILTER), 0, &getCppuType((uno::Sequence<sheet::TableFilterField>*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_FUNCTION), 0, &getCppuType((sheet::GeneralFunction*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_ISDATALA), 0, &getBooleanCppuType(), beans::PropertyAttribute::READONLY, 0 }, + {MAP_CHAR_LEN(SC_UNO_NUMBERFO), 0, &getCppuType((sal_Int32*)0), beans::PropertyAttribute::READONLY, 0 }, + {MAP_CHAR_LEN(SC_UNO_ORIENTAT), 0, &getCppuType((sheet::DataPilotFieldOrientation*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_ORIGINAL), 0, &getCppuType((uno::Reference<container::XNamed>*)0), beans::PropertyAttribute::READONLY, 0 }, + {MAP_CHAR_LEN(SC_UNO_POSITION), 0, &getCppuType((sal_Int32*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_REFVALUE), 0, &getCppuType((sheet::DataPilotFieldReference*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_USEDHIER), 0, &getCppuType((sal_Int32*)0), 0, 0 }, + {0,0,0,0,0,0} + }; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( aDPDimensionMap_Impl ); + return aRef; +} + +void SAL_CALL ScDPDimension::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) ) + { + INT32 nInt = 0; + if (aValue >>= nInt) + setPosition( nInt ); + } + else if ( aNameStr.EqualsAscii( SC_UNO_USEDHIER ) ) + { + INT32 nInt = 0; + if (aValue >>= nInt) + setUsedHierarchy( nInt ); + } + else if ( aNameStr.EqualsAscii( SC_UNO_ORIENTAT ) ) + { + sheet::DataPilotFieldOrientation eEnum; + if (aValue >>= eEnum) + setOrientation( sal::static_int_cast<USHORT>(eEnum) ); + } + else if ( aNameStr.EqualsAscii( SC_UNO_FUNCTION ) ) + { + sheet::GeneralFunction eEnum; + if (aValue >>= eEnum) + setFunction( sal::static_int_cast<USHORT>(eEnum) ); + } + else if ( aNameStr.EqualsAscii( SC_UNO_REFVALUE ) ) + aValue >>= aReferenceValue; + else if ( aNameStr.EqualsAscii( SC_UNO_FILTER ) ) + { + BOOL bDone = FALSE; + uno::Sequence<sheet::TableFilterField> aSeq; + if (aValue >>= aSeq) + { + sal_Int32 nLength = aSeq.getLength(); + if ( nLength == 0 ) + { + aSelectedPage.Erase(); + bHasSelectedPage = FALSE; + bDone = TRUE; + } + else if ( nLength == 1 ) + { + const sheet::TableFilterField& rField = aSeq[0]; + if ( rField.Field == 0 && rField.Operator == sheet::FilterOperator_EQUAL && !rField.IsNumeric ) + { + aSelectedPage = rField.StringValue; + bHasSelectedPage = TRUE; + bDone = TRUE; + } + } + } + if ( !bDone ) + { + DBG_ERROR("Filter property is not a single string"); + throw lang::IllegalArgumentException(); + } + DELETEZ( pSelectedData ); // invalid after changing aSelectedPage + } + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } +} + +uno::Any SAL_CALL ScDPDimension::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + uno::Any aRet; + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) ) + aRet <<= (sal_Int32) getPosition(); + else if ( aNameStr.EqualsAscii( SC_UNO_USEDHIER ) ) + aRet <<= (sal_Int32) getUsedHierarchy(); + else if ( aNameStr.EqualsAscii( SC_UNO_ORIENTAT ) ) + { + sheet::DataPilotFieldOrientation eVal = (sheet::DataPilotFieldOrientation)getOrientation(); + aRet <<= eVal; + } + else if ( aNameStr.EqualsAscii( SC_UNO_FUNCTION ) ) + { + sheet::GeneralFunction eVal = (sheet::GeneralFunction)getFunction(); + aRet <<= eVal; + } + else if ( aNameStr.EqualsAscii( SC_UNO_REFVALUE ) ) + aRet <<= aReferenceValue; + else if ( aNameStr.EqualsAscii( SC_UNO_ISDATALA ) ) // read-only properties + lcl_SetBoolInAny( aRet, getIsDataLayoutDimension() ); + else if ( aNameStr.EqualsAscii( SC_UNO_NUMBERFO ) ) + { + sal_Int32 nFormat = 0; + sheet::GeneralFunction eFunc = (sheet::GeneralFunction)getFunction(); + // #i63745# don't use source format for "count" + if ( eFunc != sheet::GeneralFunction_COUNT && eFunc != sheet::GeneralFunction_COUNTNUMS ) + nFormat = pSource->GetData()->GetNumberFormat( ( nSourceDim >= 0 ) ? nSourceDim : nDim ); + aRet <<= nFormat; + } + else if ( aNameStr.EqualsAscii( SC_UNO_ORIGINAL ) ) + { + uno::Reference<container::XNamed> xOriginal; + if (nSourceDim >= 0) + xOriginal = pSource->GetDimensionsObject()->getByIndex(nSourceDim); + aRet <<= xOriginal; + } + else if ( aNameStr.EqualsAscii( SC_UNO_FILTER ) ) + { + if ( bHasSelectedPage ) + { + // single filter field: first field equal to selected string + sheet::TableFilterField aField( sheet::FilterConnection_AND, 0, + sheet::FilterOperator_EQUAL, sal_False, 0.0, aSelectedPage ); + aRet <<= uno::Sequence<sheet::TableFilterField>( &aField, 1 ); + } + else + aRet <<= uno::Sequence<sheet::TableFilterField>(0); + } + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPDimension ) + +// ----------------------------------------------------------------------- + +ScDPHierarchies::ScDPHierarchies( ScDPSource* pSrc, long nD ) : + pSource( pSrc ), + nDim( nD ), + ppHiers( NULL ) +{ + //! hold pSource + +#if 0 + // date columns have 3 hierarchies (flat/quarter/week), other columns only one + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( pSource->IsDateDimension( nSrcDim ) ) + nHierCount = SC_DAPI_DATE_HIERARCHIES; + else + nHierCount = 1; +#endif + + // #i52547# don't offer the incomplete date hierarchy implementation + nHierCount = 1; +} + +ScDPHierarchies::~ScDPHierarchies() +{ + //! release pSource + + if (ppHiers) + { + for (long i=0; i<nHierCount; i++) + if ( ppHiers[i] ) + ppHiers[i]->release(); // ref-counted + delete[] ppHiers; + } +} + +// very simple XNameAccess implementation using getCount/getByIndex + +uno::Any SAL_CALL ScDPHierarchies::getByName( const rtl::OUString& aName ) + throw(container::NoSuchElementException, + lang::WrappedTargetException, uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + { + uno::Reference<container::XNamed> xNamed = getByIndex(i); + uno::Any aRet; + aRet <<= xNamed; + return aRet; + } + + throw container::NoSuchElementException(); +// return uno::Any(); +} + +uno::Sequence<rtl::OUString> SAL_CALL ScDPHierarchies::getElementNames() throw(uno::RuntimeException) +{ + long nCount = getCount(); + uno::Sequence<rtl::OUString> aSeq(nCount); + rtl::OUString* pArr = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArr[i] = getByIndex(i)->getName(); + return aSeq; +} + +sal_Bool SAL_CALL ScDPHierarchies::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + return TRUE; + return FALSE; +} + +uno::Type SAL_CALL ScDPHierarchies::getElementType() throw(uno::RuntimeException) +{ + return getCppuType((uno::Reference<container::XNamed>*)0); +} + +sal_Bool SAL_CALL ScDPHierarchies::hasElements() throw(uno::RuntimeException) +{ + return ( getCount() > 0 ); +} + +// end of XNameAccess implementation + +long ScDPHierarchies::getCount() const +{ + return nHierCount; +} + +ScDPHierarchy* ScDPHierarchies::getByIndex(long nIndex) const +{ + // pass hierarchy index to new object in case the implementation + // will be extended to more than one hierarchy + + if ( nIndex >= 0 && nIndex < nHierCount ) + { + if ( !ppHiers ) + { + ((ScDPHierarchies*)this)->ppHiers = new ScDPHierarchy*[nHierCount]; + for (long i=0; i<nHierCount; i++) + ppHiers[i] = NULL; + } + if ( !ppHiers[nIndex] ) + { + ppHiers[nIndex] = new ScDPHierarchy( pSource, nDim, nIndex ); + ppHiers[nIndex]->acquire(); // ref-counted + } + + return ppHiers[nIndex]; + } + + return NULL; //! exception? +} + +// ----------------------------------------------------------------------- + +ScDPHierarchy::ScDPHierarchy( ScDPSource* pSrc, long nD, long nH ) : + pSource( pSrc ), + nDim( nD ), + nHier( nH ), + pLevels( NULL ) +{ + //! hold pSource +} + +ScDPHierarchy::~ScDPHierarchy() +{ + //! release pSource + + if (pLevels) + pLevels->release(); // ref-counted +} + +ScDPLevels* ScDPHierarchy::GetLevelsObject() +{ + if (!pLevels) + { + pLevels = new ScDPLevels( pSource, nDim, nHier ); + pLevels->acquire(); // ref-counted + } + return pLevels; +} + +uno::Reference<container::XNameAccess> SAL_CALL ScDPHierarchy::getLevels() + throw(uno::RuntimeException) +{ + return GetLevelsObject(); +} + +::rtl::OUString SAL_CALL ScDPHierarchy::getName() throw(uno::RuntimeException) +{ + String aRet; //! globstr-ID !!!! + switch (nHier) + { + case SC_DAPI_HIERARCHY_FLAT: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("flat")); + break; //! name ??????? + case SC_DAPI_HIERARCHY_QUARTER: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Quarter")); + break; //! name ??????? + case SC_DAPI_HIERARCHY_WEEK: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Week")); + break; //! name ??????? + default: + DBG_ERROR( "ScDPHierarchy::getName: unexpected hierarchy" ); + break; + } + return aRet; +} + +void SAL_CALL ScDPHierarchy::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException) +{ + DBG_ERROR("not implemented"); //! exception? +} + +// ----------------------------------------------------------------------- + +ScDPLevels::ScDPLevels( ScDPSource* pSrc, long nD, long nH ) : + pSource( pSrc ), + nDim( nD ), + nHier( nH ), + ppLevs( NULL ) +{ + //! hold pSource + + // text columns have only one level + + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( pSource->IsDateDimension( nSrcDim ) ) + { + switch ( nHier ) + { + case SC_DAPI_HIERARCHY_FLAT: nLevCount = SC_DAPI_FLAT_LEVELS; break; + case SC_DAPI_HIERARCHY_QUARTER: nLevCount = SC_DAPI_QUARTER_LEVELS; break; + case SC_DAPI_HIERARCHY_WEEK: nLevCount = SC_DAPI_WEEK_LEVELS; break; + default: + DBG_ERROR("wrong hierarchy"); + nLevCount = 0; + } + } + else + nLevCount = 1; +} + +ScDPLevels::~ScDPLevels() +{ + //! release pSource + + if (ppLevs) + { + for (long i=0; i<nLevCount; i++) + if ( ppLevs[i] ) + ppLevs[i]->release(); // ref-counted + delete[] ppLevs; + } +} + +// very simple XNameAccess implementation using getCount/getByIndex + +uno::Any SAL_CALL ScDPLevels::getByName( const rtl::OUString& aName ) + throw(container::NoSuchElementException, + lang::WrappedTargetException, uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + { + uno::Reference<container::XNamed> xNamed = getByIndex(i); + uno::Any aRet; + aRet <<= xNamed; + return aRet; + } + + throw container::NoSuchElementException(); +// return uno::Any(); +} + +uno::Sequence<rtl::OUString> SAL_CALL ScDPLevels::getElementNames() throw(uno::RuntimeException) +{ + long nCount = getCount(); + uno::Sequence<rtl::OUString> aSeq(nCount); + rtl::OUString* pArr = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArr[i] = getByIndex(i)->getName(); + return aSeq; +} + +sal_Bool SAL_CALL ScDPLevels::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException) +{ + long nCount = getCount(); + for (long i=0; i<nCount; i++) + if ( getByIndex(i)->getName() == aName ) + return TRUE; + return FALSE; +} + +uno::Type SAL_CALL ScDPLevels::getElementType() throw(uno::RuntimeException) +{ + return getCppuType((uno::Reference<container::XNamed>*)0); +} + +sal_Bool SAL_CALL ScDPLevels::hasElements() throw(uno::RuntimeException) +{ + return ( getCount() > 0 ); +} + +// end of XNameAccess implementation + +long ScDPLevels::getCount() const +{ + return nLevCount; +} + +ScDPLevel* ScDPLevels::getByIndex(long nIndex) const +{ + if ( nIndex >= 0 && nIndex < nLevCount ) + { + if ( !ppLevs ) + { + ((ScDPLevels*)this)->ppLevs = new ScDPLevel*[nLevCount]; + for (long i=0; i<nLevCount; i++) + ppLevs[i] = NULL; + } + if ( !ppLevs[nIndex] ) + { + ppLevs[nIndex] = new ScDPLevel( pSource, nDim, nHier, nIndex ); + ppLevs[nIndex]->acquire(); // ref-counted + } + + return ppLevs[nIndex]; + } + + return NULL; //! exception? +} + +// ----------------------------------------------------------------------- + +class ScDPGlobalMembersOrder +{ + ScDPLevel& rLevel; + BOOL bAscending; + +public: + ScDPGlobalMembersOrder( ScDPLevel& rLev, BOOL bAsc ) : + rLevel(rLev), + bAscending(bAsc) + {} + ~ScDPGlobalMembersOrder() {} + + BOOL operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const; +}; + +BOOL ScDPGlobalMembersOrder::operator()( sal_Int32 nIndex1, sal_Int32 nIndex2 ) const +{ + sal_Int32 nCompare = 0; + // seems that some ::std::sort() implementations pass the same index twice + if( nIndex1 != nIndex2 ) + { + ScDPMembers* pMembers = rLevel.GetMembersObject(); + ScDPMember* pMember1 = pMembers->getByIndex(nIndex1); + ScDPMember* pMember2 = pMembers->getByIndex(nIndex2); + nCompare = pMember1->Compare( *pMember2 ); + } + return bAscending ? (nCompare < 0) : (nCompare > 0); +} + +// ----------------------------------------------------------------------- + +ScDPLevel::ScDPLevel( ScDPSource* pSrc, long nD, long nH, long nL ) : + pSource( pSrc ), + nDim( nD ), + nHier( nH ), + nLev( nL ), + pMembers( NULL ), + bShowEmpty( FALSE ), + aSortInfo( EMPTY_STRING, sal_True, sheet::DataPilotFieldSortMode::NAME ), // default: sort by name + nSortMeasure( 0 ), + nAutoMeasure( 0 ), + bEnableLayout( FALSE ) +{ + //! hold pSource + // aSubTotals is empty +} + +ScDPLevel::~ScDPLevel() +{ + //! release pSource + + if ( pMembers ) + pMembers->release(); // ref-counted +} + +void ScDPLevel::EvaluateSortOrder() +{ + switch (aSortInfo.Mode) + { + case sheet::DataPilotFieldSortMode::DATA: + { + // find index of measure (index among data dimensions) + + String aDataFieldName = aSortInfo.Field; + long nMeasureCount = pSource->GetDataDimensionCount(); + for (long nMeasure=0; nMeasure<nMeasureCount; nMeasure++) + { + if ( pSource->GetDataDimName(nMeasure) == aDataFieldName ) + { + nSortMeasure = nMeasure; + break; + } + } + + //! error if not found? + } + break; + case sheet::DataPilotFieldSortMode::MANUAL: + case sheet::DataPilotFieldSortMode::NAME: + { + ScDPMembers* pLocalMembers = GetMembersObject(); + long nCount = pLocalMembers->getCount(); + +// DBG_ASSERT( aGlobalOrder.empty(), "sort twice?" ); + aGlobalOrder.resize( nCount ); + for (long nPos=0; nPos<nCount; nPos++) + aGlobalOrder[nPos] = nPos; + + // allow manual or name (manual is always ascending) + BOOL bAscending = ( aSortInfo.Mode == sheet::DataPilotFieldSortMode::MANUAL || aSortInfo.IsAscending ); + ScDPGlobalMembersOrder aComp( *this, bAscending ); + ::std::sort( aGlobalOrder.begin(), aGlobalOrder.end(), aComp ); + } + break; + } + + if ( aAutoShowInfo.IsEnabled ) + { + // find index of measure (index among data dimensions) + + String aDataFieldName = aAutoShowInfo.DataField; + long nMeasureCount = pSource->GetDataDimensionCount(); + for (long nMeasure=0; nMeasure<nMeasureCount; nMeasure++) + { + if ( pSource->GetDataDimName(nMeasure) == aDataFieldName ) + { + nAutoMeasure = nMeasure; + break; + } + } + + //! error if not found? + } +} + +void ScDPLevel::SetEnableLayout( BOOL bSet ) +{ + bEnableLayout = bSet; +} + +ScDPMembers* ScDPLevel::GetMembersObject() +{ + if (!pMembers) + { + pMembers = new ScDPMembers( pSource, nDim, nHier, nLev ); + pMembers->acquire(); // ref-counted + } + return pMembers; +} + +uno::Reference<container::XNameAccess> SAL_CALL ScDPLevel::getMembers() throw(uno::RuntimeException) +{ + return GetMembersObject(); +} + +uno::Sequence<sheet::MemberResult> SAL_CALL ScDPLevel::getResults() throw(uno::RuntimeException) +{ + const uno::Sequence<sheet::MemberResult>* pRes = pSource->GetMemberResults( this ); + if (pRes) + return *pRes; + + return uno::Sequence<sheet::MemberResult>(0); //! Error? +} + +::rtl::OUString SAL_CALL ScDPLevel::getName() throw(uno::RuntimeException) +{ + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( pSource->IsDateDimension( nSrcDim ) ) + { + String aRet; //! globstr-ID !!!! + + if ( nHier == SC_DAPI_HIERARCHY_QUARTER ) + { + switch ( nLev ) + { + case SC_DAPI_LEVEL_YEAR: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Year")); + break; + case SC_DAPI_LEVEL_QUARTER: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Quarter")); + break; + case SC_DAPI_LEVEL_MONTH: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Month")); + break; + case SC_DAPI_LEVEL_DAY: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Day")); + break; + default: + DBG_ERROR( "ScDPLevel::getName: unexpected level" ); + break; + } + } + else if ( nHier == SC_DAPI_HIERARCHY_WEEK ) + { + switch ( nLev ) + { + case SC_DAPI_LEVEL_YEAR: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Year")); + break; + case SC_DAPI_LEVEL_WEEK: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Week")); + break; + case SC_DAPI_LEVEL_WEEKDAY: + aRet = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Weekday")); + break; + default: + DBG_ERROR( "ScDPLevel::getName: unexpected level" ); + break; + } + } + if (aRet.Len()) + return aRet; + } + + return pSource->GetData()->getDimensionName( nSrcDim ); // (original) dimension name +} + +void SAL_CALL ScDPLevel::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException) +{ + DBG_ERROR("not implemented"); //! exception? +} + +uno::Sequence<sheet::GeneralFunction> ScDPLevel::getSubTotals() const +{ + //! separate functions for settings and evaluation? + + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( !pSource->SubTotalAllowed( nSrcDim ) ) + return uno::Sequence<sheet::GeneralFunction>(0); + + return aSubTotals; +} + +void ScDPLevel::setSubTotals(const uno::Sequence<sheet::GeneralFunction>& rNew) +{ + aSubTotals = rNew; + //! set "manual change" flag? +} + +BOOL ScDPLevel::getShowEmpty() const +{ + return bShowEmpty; +} + +void ScDPLevel::setShowEmpty(BOOL bSet) +{ + bShowEmpty = bSet; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPLevel::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ScUnoGuard aGuard; + + static SfxItemPropertyMapEntry aDPLevelMap_Impl[] = + { + //! change type of AutoShow/Layout/Sorting to API struct when available + {MAP_CHAR_LEN(SC_UNO_AUTOSHOW), 0, &getCppuType((sheet::DataPilotFieldAutoShowInfo*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_LAYOUT), 0, &getCppuType((sheet::DataPilotFieldLayoutInfo*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_SHOWEMPT), 0, &getBooleanCppuType(), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_SORTING), 0, &getCppuType((sheet::DataPilotFieldSortInfo*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_SUBTOTAL), 0, &getCppuType((uno::Sequence<sheet::GeneralFunction>*)0), 0, 0 }, + {0,0,0,0,0,0} + }; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( aDPLevelMap_Impl ); + return aRef; +} + +void SAL_CALL ScDPLevel::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_SHOWEMPT ) ) + setShowEmpty( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_SUBTOTAL ) ) + { + uno::Sequence<sheet::GeneralFunction> aSeq; + if ( aValue >>= aSeq ) + setSubTotals( aSeq ); + } + else if ( aNameStr.EqualsAscii( SC_UNO_SORTING ) ) + aValue >>= aSortInfo; + else if ( aNameStr.EqualsAscii( SC_UNO_AUTOSHOW ) ) + aValue >>= aAutoShowInfo; + else if ( aNameStr.EqualsAscii( SC_UNO_LAYOUT ) ) + aValue >>= aLayoutInfo; + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } +} + +uno::Any SAL_CALL ScDPLevel::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + uno::Any aRet; + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_SHOWEMPT ) ) + lcl_SetBoolInAny( aRet, getShowEmpty() ); + else if ( aNameStr.EqualsAscii( SC_UNO_SUBTOTAL ) ) + { + uno::Sequence<sheet::GeneralFunction> aSeq = getSubTotals(); //! avoid extra copy? + aRet <<= aSeq; + } + else if ( aNameStr.EqualsAscii( SC_UNO_SORTING ) ) + aRet <<= aSortInfo; + else if ( aNameStr.EqualsAscii( SC_UNO_AUTOSHOW ) ) + aRet <<= aAutoShowInfo; + else if ( aNameStr.EqualsAscii( SC_UNO_LAYOUT ) ) + aRet <<= aLayoutInfo; + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPLevel ) + +// ----------------------------------------------------------------------- + +USHORT lcl_GetFirstStringPos( const TypedScStrCollection& rColl ) +{ + USHORT nPos = 0; + USHORT nCount = rColl.GetCount(); + while ( nPos < nCount && !rColl[nPos]->IsStrData() ) + ++nPos; + return nPos; +} + +ScDPMembers::ScDPMembers( ScDPSource* pSrc, long nD, long nH, long nL ) : + pSource( pSrc ), + nDim( nD ), + nHier( nH ), + nLev( nL ), + ppMbrs( NULL ) +{ + //! hold pSource + + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( pSource->IsDataLayoutDimension(nSrcDim) ) + nMbrCount = pSource->GetDataDimensionCount(); + else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) ) + { + nMbrCount = 0; + if ( nHier == SC_DAPI_HIERARCHY_QUARTER ) + { + switch (nLev) + { + case SC_DAPI_LEVEL_YEAR: + { + const TypedScStrCollection& rStrings = pSource->GetData()->GetColumnEntries(nSrcDim); + USHORT nFirstString = lcl_GetFirstStringPos( rStrings ); + if ( nFirstString > 0 ) + { + double fFirstVal = rStrings[0]->GetValue(); + double fLastVal = rStrings[nFirstString-1]->GetValue(); + + long nFirstYear = pSource->GetData()->GetDatePart( + (long)::rtl::math::approxFloor( fFirstVal ), + nHier, nLev ); + long nLastYear = pSource->GetData()->GetDatePart( + (long)::rtl::math::approxFloor( fLastVal ), + nHier, nLev ); + + nMbrCount = nLastYear + 1 - nFirstYear; + } + else + nMbrCount = 0; // no values + } + break; + case SC_DAPI_LEVEL_QUARTER: nMbrCount = 4; break; + case SC_DAPI_LEVEL_MONTH: nMbrCount = 12; break; + case SC_DAPI_LEVEL_DAY: nMbrCount = 31; break; + default: + DBG_ERROR( "ScDPMembers::ScDPMembers: unexpected level" ); + break; + } + } + else if ( nHier == SC_DAPI_HIERARCHY_WEEK ) + { + switch (nLev) + { + case SC_DAPI_LEVEL_YEAR: nMbrCount = 1; break; //! get years from source + case SC_DAPI_LEVEL_WEEK: nMbrCount = 53; break; + case SC_DAPI_LEVEL_WEEKDAY: nMbrCount = 7; break; + default: + DBG_ERROR( "ScDPMembers::ScDPMembers: unexpected level" ); + break; + } + } + } + else + { + // StringCollection is cached at TableData + const TypedScStrCollection& rStrings = pSource->GetData()->GetColumnEntries(nSrcDim); + nMbrCount = rStrings.GetCount(); + } +} + +ScDPMembers::~ScDPMembers() +{ + //! release pSource + + if (ppMbrs) + { + for (long i=0; i<nMbrCount; i++) + if ( ppMbrs[i] ) + ppMbrs[i]->release(); // ref-counted + delete[] ppMbrs; + } +} + +// XNameAccess implementation using getCount/getByIndex + +sal_Int32 ScDPMembers::GetIndexFromName( const ::rtl::OUString& rName ) const +{ + if ( aHashMap.empty() ) + { + // store the index for each name + + sal_Int32 nCount = getCount(); + for (sal_Int32 i=0; i<nCount; i++) + aHashMap[ getByIndex(i)->getName() ] = i; + } + + ScDPMembersHashMap::const_iterator aIter = aHashMap.find( rName ); + if ( aIter != aHashMap.end() ) + return aIter->second; // found index + else + return -1; // not found +} + +uno::Any SAL_CALL ScDPMembers::getByName( const rtl::OUString& aName ) + throw(container::NoSuchElementException, + lang::WrappedTargetException, uno::RuntimeException) +{ + sal_Int32 nIndex = GetIndexFromName( aName ); + if ( nIndex >= 0 ) + { + uno::Reference<container::XNamed> xNamed = getByIndex(nIndex); + uno::Any aRet; + aRet <<= xNamed; + return aRet; + } + + throw container::NoSuchElementException(); +// return uno::Any(); +} + +uno::Sequence<rtl::OUString> SAL_CALL ScDPMembers::getElementNames() throw(uno::RuntimeException) +{ + // Return list of names in sorted order, + // so it's displayed in that order in the field options dialog. + // Sorting is done at the level object (parent of this). + + ScDPLevel* pLevel = pSource->GetDimensionsObject()->getByIndex(nDim)-> + GetHierarchiesObject()->getByIndex(nHier)->GetLevelsObject()->getByIndex(nLev); + pLevel->EvaluateSortOrder(); + const std::vector<sal_Int32>& rGlobalOrder = pLevel->GetGlobalOrder(); + bool bSort = !rGlobalOrder.empty(); + + long nCount = getCount(); + uno::Sequence<rtl::OUString> aSeq(nCount); + rtl::OUString* pArr = aSeq.getArray(); + for (long i=0; i<nCount; i++) + pArr[i] = getByIndex(bSort ? rGlobalOrder[i] : i)->getName(); + return aSeq; +} + +sal_Bool SAL_CALL ScDPMembers::hasByName( const rtl::OUString& aName ) throw(uno::RuntimeException) +{ + return ( GetIndexFromName( aName ) >= 0 ); +} + +uno::Type SAL_CALL ScDPMembers::getElementType() throw(uno::RuntimeException) +{ + return getCppuType((uno::Reference<container::XNamed>*)0); +} + +sal_Bool SAL_CALL ScDPMembers::hasElements() throw(uno::RuntimeException) +{ + return ( getCount() > 0 ); +} + +// end of XNameAccess implementation + +long ScDPMembers::getCount() const +{ + return nMbrCount; +} + +long ScDPMembers::getMinMembers() const +{ + // used in lcl_CountMinMembers + + long nVisCount = 0; + if ( ppMbrs ) + { + for (long i=0; i<nMbrCount; i++) + { + // count only visible with details (default is true for both) + const ScDPMember* pMbr = ppMbrs[i]; + if ( !pMbr || ( pMbr->getIsVisible() && pMbr->getShowDetails() ) ) + ++nVisCount; + } + } + else + nVisCount = nMbrCount; // default for all + + return nVisCount; +} + +ScDPMember* ScDPMembers::getByIndex(long nIndex) const +{ + // result of GetColumnEntries must not change between ScDPMembers ctor + // and all calls to getByIndex + + if ( nIndex >= 0 && nIndex < nMbrCount ) + { + if ( !ppMbrs ) + { + ((ScDPMembers*)this)->ppMbrs = new ScDPMember*[nMbrCount]; + for (long i=0; i<nMbrCount; i++) + ppMbrs[i] = NULL; + } + if ( !ppMbrs[nIndex] ) + { + ScDPMember* pNew; + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( pSource->IsDataLayoutDimension(nSrcDim) ) + { + // empty name (never shown, not used for lookup) + pNew = new ScDPMember( pSource, nDim, nHier, nLev, + String(), 0.0, FALSE ); + } + else if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) ) + { + long nVal = 0; + String aName; + + if ( nLev == SC_DAPI_LEVEL_YEAR ) // YEAR is in both hierarchies + { + //! cache year range here! + + const TypedScStrCollection& rStrings = pSource->GetData()->GetColumnEntries(nSrcDim); + double fFirstVal = rStrings[0]->GetValue(); + long nFirstYear = pSource->GetData()->GetDatePart( + (long)::rtl::math::approxFloor( fFirstVal ), + nHier, nLev ); + + nVal = nFirstYear + nIndex; + } + else if ( nHier == SC_DAPI_HIERARCHY_WEEK && nLev == SC_DAPI_LEVEL_WEEKDAY ) + { + nVal = nIndex; // DayOfWeek is 0-based + aName = ScGlobal::GetCalendar()->getDisplayName( + ::com::sun::star::i18n::CalendarDisplayIndex::DAY, + sal::static_int_cast<sal_Int16>(nVal), 0 ); + } + else if ( nHier == SC_DAPI_HIERARCHY_QUARTER && nLev == SC_DAPI_LEVEL_MONTH ) + { + nVal = nIndex; // Month is 0-based + aName = ScGlobal::GetCalendar()->getDisplayName( + ::com::sun::star::i18n::CalendarDisplayIndex::MONTH, + sal::static_int_cast<sal_Int16>(nVal), 0 ); + } + else + nVal = nIndex + 1; // Quarter, Day, Week are 1-based + + if ( !aName.Len() ) + aName = String::CreateFromInt32(nVal); + + pNew = new ScDPMember( pSource, nDim, nHier, nLev, aName, nVal, TRUE ); + } + else + { + const TypedScStrCollection& rStrings = pSource->GetData()->GetColumnEntries(nSrcDim); + const TypedStrData* pData = rStrings[(USHORT)nIndex]; + pNew = new ScDPMember( pSource, nDim, nHier, nLev, + pData->GetString(), pData->GetValue(), !pData->IsStrData() ); + } + pNew->acquire(); // ref-counted + ppMbrs[nIndex] = pNew; + } + + return ppMbrs[nIndex]; + } + + return NULL; //! exception? +} + +// ----------------------------------------------------------------------- + +ScDPMember::ScDPMember( ScDPSource* pSrc, long nD, long nH, long nL, + const String& rN, double fV, BOOL bHV ) : + pSource( pSrc ), + nDim( nD ), + nHier( nH ), + nLev( nL ), + maData( rN, fV, bHV ), + nPosition( -1 ), + bVisible( TRUE ), + bShowDet( TRUE ) +{ + //! hold pSource +} + +ScDPMember::~ScDPMember() +{ + //! release pSource +} + +BOOL ScDPMember::IsNamedItem( const ScDPItemData& r ) const +{ + long nSrcDim = pSource->GetSourceDim( nDim ); + if ( nHier != SC_DAPI_HIERARCHY_FLAT && pSource->IsDateDimension( nSrcDim ) && r.bHasValue ) + { + long nComp = pSource->GetData()->GetDatePart( + (long)::rtl::math::approxFloor( r.fValue ), + nHier, nLev ); + + // fValue is converted from integer, so simple comparison works + return nComp == maData.fValue; + } + + return r.IsCaseInsEqual( maData ); +} + +sal_Int32 ScDPMember::Compare( const ScDPMember& rOther ) const +{ + if ( nPosition >= 0 ) + { + if ( rOther.nPosition >= 0 ) + { + DBG_ASSERT( nPosition != rOther.nPosition, "same position for two members" ); + return ( nPosition < rOther.nPosition ) ? -1 : 1; + } + else + { + // only this has a position - members with specified positions come before those without + return -1; + } + } + else if ( rOther.nPosition >= 0 ) + { + // only rOther has a position + return 1; + } + + // no positions set - compare names + return ScDPItemData::Compare( maData, rOther.maData ); +} + +void ScDPMember::FillItemData( ScDPItemData& rData ) const +{ + //! handle date hierarchy... + + rData = maData; +} + +String ScDPMember::GetNameStr() const +{ + return maData.aString; +} + +::rtl::OUString SAL_CALL ScDPMember::getName() throw(uno::RuntimeException) +{ + return maData.aString; +} + +void SAL_CALL ScDPMember::setName( const ::rtl::OUString& /* rNewName */ ) throw(uno::RuntimeException) +{ + DBG_ERROR("not implemented"); //! exception? +} + +BOOL ScDPMember::getIsVisible() const +{ + return bVisible; +} + +void ScDPMember::setIsVisible(BOOL bSet) +{ + bVisible = bSet; + //! set "manual change" flag +} + +BOOL ScDPMember::getShowDetails() const +{ + return bShowDet; +} + +void ScDPMember::setShowDetails(BOOL bSet) +{ + bShowDet = bSet; + //! set "manual change" flag +} + +sal_Int32 ScDPMember::getPosition() const +{ + return nPosition; +} + +void ScDPMember::setPosition(sal_Int32 nNew) +{ + nPosition = nNew; +} + +// XPropertySet + +uno::Reference<beans::XPropertySetInfo> SAL_CALL ScDPMember::getPropertySetInfo() + throw(uno::RuntimeException) +{ + ScUnoGuard aGuard; + + static SfxItemPropertyMapEntry aDPMemberMap_Impl[] = + { + {MAP_CHAR_LEN(SC_UNO_ISVISIBL), 0, &getBooleanCppuType(), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_POSITION), 0, &getCppuType((sal_Int32*)0), 0, 0 }, + {MAP_CHAR_LEN(SC_UNO_SHOWDETA), 0, &getBooleanCppuType(), 0, 0 }, + {0,0,0,0,0,0} + }; + static uno::Reference<beans::XPropertySetInfo> aRef = + new SfxItemPropertySetInfo( aDPMemberMap_Impl ); + return aRef; +} + +void SAL_CALL ScDPMember::setPropertyValue( const rtl::OUString& aPropertyName, const uno::Any& aValue ) + throw(beans::UnknownPropertyException, beans::PropertyVetoException, + lang::IllegalArgumentException, lang::WrappedTargetException, + uno::RuntimeException) +{ + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_ISVISIBL ) ) + setIsVisible( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_SHOWDETA ) ) + setShowDetails( lcl_GetBoolFromAny( aValue ) ); + else if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) ) + { + sal_Int32 nInt = 0; + if (aValue >>= nInt) + setPosition( nInt ); + } + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } +} + +uno::Any SAL_CALL ScDPMember::getPropertyValue( const rtl::OUString& aPropertyName ) + throw(beans::UnknownPropertyException, lang::WrappedTargetException, + uno::RuntimeException) +{ + uno::Any aRet; + String aNameStr = aPropertyName; + if ( aNameStr.EqualsAscii( SC_UNO_ISVISIBL ) ) + lcl_SetBoolInAny( aRet, getIsVisible() ); + else if ( aNameStr.EqualsAscii( SC_UNO_SHOWDETA ) ) + lcl_SetBoolInAny( aRet, getShowDetails() ); + else if ( aNameStr.EqualsAscii( SC_UNO_POSITION ) ) + aRet <<= (sal_Int32) getPosition(); + else + { + DBG_ERROR("unknown property"); + //! THROW( UnknownPropertyException() ); + } + return aRet; +} + +SC_IMPL_DUMMY_PROPERTY_LISTENER( ScDPMember ) + + + diff --git a/sc/source/core/data/drawpage.cxx b/sc/source/core/data/drawpage.cxx new file mode 100644 index 000000000000..909d1e6111a9 --- /dev/null +++ b/sc/source/core/data/drawpage.cxx @@ -0,0 +1,68 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: drawpage.cxx,v $ + * $Revision: 1.10 $ + * + * 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 <sfx2/objsh.hxx> + +#include "drawpage.hxx" +#include "drwlayer.hxx" +#include "document.hxx" +#include "pageuno.hxx" + +// STATIC DATA ----------------------------------------------------------- + +// ----------------------------------------------------------------------- + +ScDrawPage::ScDrawPage(ScDrawLayer& rNewModel, StarBASIC* pBasic, BOOL bMasterPage) : + FmFormPage(rNewModel, pBasic, bMasterPage) +{ + SetSize( Size( LONG_MAX, LONG_MAX ) ); +} + +// ----------------------------------------------------------------------- + +__EXPORT ScDrawPage::~ScDrawPage() +{ +} + +// ----------------------------------------------------------------------- + +::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > ScDrawPage::createUnoPage() +{ + return static_cast<cppu::OWeakObject*>( new ScPageObj( this ) ); +} + + diff --git a/sc/source/core/data/drwlayer.cxx b/sc/source/core/data/drwlayer.cxx new file mode 100644 index 000000000000..a5c571026e96 --- /dev/null +++ b/sc/source/core/data/drwlayer.cxx @@ -0,0 +1,2075 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: drwlayer.cxx,v $ + * $Revision: 1.55.128.8 $ + * + * 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 <com/sun/star/uno/Reference.hxx> +#include <com/sun/star/embed/XEmbeddedObject.hpp> +#include <com/sun/star/embed/XVisualObject.hpp> +#include <com/sun/star/embed/XClassifiedObject.hpp> +#include <com/sun/star/embed/XComponentSupplier.hpp> +#include <com/sun/star/embed/EmbedStates.hpp> +#include <com/sun/star/embed/ElementModes.hpp> +#include <com/sun/star/embed/NoVisualAreaSizeException.hpp> +#include <com/sun/star/datatransfer/XTransferable.hpp> + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svx/eeitem.hxx> +#include <svx/frmdiritem.hxx> +#include <sot/exchange.hxx> +#include <svx/objfac3d.hxx> +#include <svx/xtable.hxx> +#include <svx/svdoutl.hxx> +#include <svx/svditer.hxx> +#include <svx/svdocapt.hxx> +#include <svx/svdocirc.hxx> +#include <svx/svdoedge.hxx> +#include <svx/svdograf.hxx> +#include <svx/svdoole2.hxx> +#include <svx/svdundo.hxx> +#include <svx/unolingu.hxx> +#include <svx/drawitem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/scriptspaceitem.hxx> +#include <svx/shapepropertynotifier.hxx> +#include <sfx2/viewsh.hxx> +#include <sfx2/docfile.hxx> +#include <sot/storage.hxx> +#include <svtools/pathoptions.hxx> +#include <svtools/itempool.hxx> +#include <vcl/virdev.hxx> +#include <vcl/svapp.hxx> +#include <unotools/ucbstreamhelper.hxx> + +#include "drwlayer.hxx" +#include "drawpage.hxx" +#include "global.hxx" +#include "document.hxx" +#include "rechead.hxx" +#include "userdat.hxx" +#include "markdata.hxx" +#include "globstr.hrc" +#include "scmod.hxx" +#include "chartarr.hxx" +#include "postit.hxx" +#include "attrib.hxx" + +#define DET_ARROW_OFFSET 1000 + +// Abstand zur naechsten Zelle beim Loeschen (bShrink), damit der Anker +// immer an der richtigen Zelle angezeigt wird +//#define SHRINK_DIST 3 +// und noch etwas mehr, damit das Objekt auch sichtbar in der Zelle liegt +#define SHRINK_DIST 25 + +#define SHRINK_DIST_TWIPS 15 + +using namespace ::com::sun::star; + +// STATIC DATA ----------------------------------------------------------- + +TYPEINIT1(ScTabDeletedHint, SfxHint); +TYPEINIT1(ScTabSizeChangedHint, SfxHint); + +static ScDrawObjFactory* pFac = NULL; +static E3dObjFactory* pF3d = NULL; +static USHORT nInst = 0; + +SfxObjectShell* ScDrawLayer::pGlobalDrawPersist = NULL; +//REMOVE SvPersist* ScDrawLayer::pGlobalDrawPersist = NULL; + +BOOL bDrawIsInUndo = FALSE; //! Member + +// ----------------------------------------------------------------------- + +ScUndoObjData::ScUndoObjData( SdrObject* pObjP, const ScAddress& rOS, const ScAddress& rOE, + const ScAddress& rNS, const ScAddress& rNE ) : + SdrUndoObj( *pObjP ), + aOldStt( rOS ), + aOldEnd( rOE ), + aNewStt( rNS ), + aNewEnd( rNE ) +{ +} + +__EXPORT ScUndoObjData::~ScUndoObjData() +{ +} + +void ScUndoObjData::Undo() +{ + ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj ); + DBG_ASSERT(pData,"ScUndoObjData: Daten nicht da"); + if (pData) + { + pData->maStart = aOldStt; + pData->maEnd = aOldEnd; + } +} + +void __EXPORT ScUndoObjData::Redo() +{ + ScDrawObjData* pData = ScDrawLayer::GetObjData( pObj ); + DBG_ASSERT(pData,"ScUndoObjData: Daten nicht da"); + if (pData) + { + pData->maStart = aNewStt; + pData->maEnd = aNewEnd; + } +} + +// ----------------------------------------------------------------------- + +ScTabDeletedHint::ScTabDeletedHint( SCTAB nTabNo ) : + nTab( nTabNo ) +{ +} + +__EXPORT ScTabDeletedHint::~ScTabDeletedHint() +{ +} + +ScTabSizeChangedHint::ScTabSizeChangedHint( SCTAB nTabNo ) : + nTab( nTabNo ) +{ +} + +__EXPORT ScTabSizeChangedHint::~ScTabSizeChangedHint() +{ +} + +// ----------------------------------------------------------------------- + +#define MAXMM 10000000 + +inline void TwipsToMM( long& nVal ) +{ + nVal = (long) ( nVal * HMM_PER_TWIPS ); +} + +inline void ReverseTwipsToMM( long& nVal ) +{ + // reverse the effect of TwipsToMM - round up here (add 1) + + nVal = ((long) ( nVal / HMM_PER_TWIPS )) + 1; +} + +void lcl_TwipsToMM( Point& rPoint ) +{ + TwipsToMM( rPoint.X() ); + TwipsToMM( rPoint.Y() ); +} + +void lcl_ReverseTwipsToMM( Point& rPoint ) +{ + ReverseTwipsToMM( rPoint.X() ); + ReverseTwipsToMM( rPoint.Y() ); +} + +void lcl_ReverseTwipsToMM( Rectangle& rRect ) +{ + ReverseTwipsToMM( rRect.Left() ); + ReverseTwipsToMM( rRect.Right() ); + ReverseTwipsToMM( rRect.Top() ); + ReverseTwipsToMM( rRect.Bottom() ); +} + +// ----------------------------------------------------------------------- + + +ScDrawLayer::ScDrawLayer( ScDocument* pDocument, const String& rName ) : + FmFormModel( SvtPathOptions().GetPalettePath(), + NULL, // SfxItemPool* Pool + pGlobalDrawPersist ? + pGlobalDrawPersist : + ( pDocument ? pDocument->GetDocumentShell() : NULL ), + TRUE ), // bUseExtColorTable (is set below) + aName( rName ), + pDoc( pDocument ), + pUndoGroup( NULL ), + bRecording( FALSE ), + bAdjustEnabled( TRUE ), + bHyphenatorSet( FALSE ) +{ + pGlobalDrawPersist = NULL; // nur einmal benutzen + + SfxObjectShell* pObjSh = pDocument ? pDocument->GetDocumentShell() : NULL; + if ( pObjSh ) + { + SetObjectShell( pObjSh ); + + // set color table + SvxColorTableItem* pColItem = (SvxColorTableItem*) pObjSh->GetItem( SID_COLOR_TABLE ); + XColorTable* pXCol = pColItem ? pColItem->GetColorTable() : XColorTable::GetStdColorTable(); + SetColorTable( pXCol ); + } + else + SetColorTable( XColorTable::GetStdColorTable() ); + + SetSwapGraphics(TRUE); +// SetSwapAsynchron(TRUE); // an der View + + SetScaleUnit(MAP_100TH_MM); + SfxItemPool& rPool = GetItemPool(); + rPool.SetDefaultMetric(SFX_MAPUNIT_100TH_MM); + SvxFrameDirectionItem aModeItem( FRMDIR_ENVIRONMENT, EE_PARA_WRITINGDIR ); + rPool.SetPoolDefaultItem( aModeItem ); + + // #i33700# + // Set shadow distance defaults as PoolDefaultItems. Details see bug. + rPool.SetPoolDefaultItem(SdrShadowXDistItem(300)); + rPool.SetPoolDefaultItem(SdrShadowYDistItem(300)); + + // #111216# default for script spacing depends on locale, see SdDrawDocument ctor in sd + LanguageType eOfficeLanguage = Application::GetSettings().GetLanguage(); + if ( eOfficeLanguage == LANGUAGE_KOREAN || eOfficeLanguage == LANGUAGE_KOREAN_JOHAB || + eOfficeLanguage == LANGUAGE_JAPANESE ) + { + // secondary is edit engine pool + rPool.GetSecondaryPool()->SetPoolDefaultItem( SvxScriptSpaceItem( FALSE, EE_PARA_ASIANCJKSPACING ) ); + } + + rPool.FreezeIdRanges(); // the pool is also used directly + + SdrLayerAdmin& rAdmin = GetLayerAdmin(); + rAdmin.NewLayer(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("vorne")), SC_LAYER_FRONT); + rAdmin.NewLayer(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("hinten")), SC_LAYER_BACK); + rAdmin.NewLayer(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("intern")), SC_LAYER_INTERN); + rAdmin.NewLayer(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("Controls")), SC_LAYER_CONTROLS); + rAdmin.NewLayer(String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("hidden")), SC_LAYER_HIDDEN); + // "Controls" is new - must also be created when loading + + // Link fuer URL-Fields setzen + ScModule* pScMod = SC_MOD(); + Outliner& rOutliner = GetDrawOutliner(); + rOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); + + Outliner& rHitOutliner = GetHitTestOutliner(); + rHitOutliner.SetCalcFieldValueHdl( LINK( pScMod, ScModule, CalcFieldValueHdl ) ); + + // #95129# SJ: set FontHeight pool defaults without changing static SdrEngineDefaults + SfxItemPool* pOutlinerPool = rOutliner.GetEditTextObjectPool(); + if ( pOutlinerPool ) + pItemPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt + SfxItemPool* pHitOutlinerPool = rHitOutliner.GetEditTextObjectPool(); + if ( pHitOutlinerPool ) + pHitOutlinerPool->SetPoolDefaultItem(SvxFontHeightItem( 423, 100, EE_CHAR_FONTHEIGHT )); // 12Pt + + // URL-Buttons haben keinen Handler mehr, machen alles selber + + if( !nInst++ ) + { + pFac = new ScDrawObjFactory; + pF3d = new E3dObjFactory; + } +} + +__EXPORT ScDrawLayer::~ScDrawLayer() +{ + Broadcast(SdrHint(HINT_MODELCLEARED)); + + // #116168# + //Clear(); + ClearModel(sal_True); + + delete pUndoGroup; + if( !--nInst ) + { + delete pFac, pFac = NULL; + delete pF3d, pF3d = NULL; + } +} + +void ScDrawLayer::UseHyphenator() +{ + if (!bHyphenatorSet) + { + com::sun::star::uno::Reference< com::sun::star::linguistic2::XHyphenator > + xHyphenator = LinguMgr::GetHyphenator(); + + GetDrawOutliner().SetHyphenator( xHyphenator ); + GetHitTestOutliner().SetHyphenator( xHyphenator ); + + bHyphenatorSet = TRUE; + } +} + +SdrPage* __EXPORT ScDrawLayer::AllocPage(FASTBOOL bMasterPage) +{ + // don't create basic until it is needed + StarBASIC* pBasic = NULL; + ScDrawPage* pPage = new ScDrawPage( *this, pBasic, sal::static_int_cast<BOOL>(bMasterPage) ); + return pPage; +} + +BOOL ScDrawLayer::HasObjects() const +{ + BOOL bFound = FALSE; + + USHORT nCount = GetPageCount(); + for (USHORT i=0; i<nCount && !bFound; i++) + if (GetPage(i)->GetObjCount()) + bFound = TRUE; + + return bFound; +} + +void ScDrawLayer::UpdateBasic() +{ + // don't create basic until it is needed + //! remove this method? +} + +SdrModel* __EXPORT ScDrawLayer::AllocModel() const +{ + // #103849# Allocated model (for clipboard etc) must not have a pointer + // to the original model's document, pass NULL as document: + + return new ScDrawLayer( NULL, aName ); +} + +Window* __EXPORT ScDrawLayer::GetCurDocViewWin() +{ + DBG_ASSERT( pDoc, "ScDrawLayer::GetCurDocViewWin without document" ); + if ( !pDoc ) + return NULL; + + SfxViewShell* pViewSh = SfxViewShell::Current(); + SfxObjectShell* pObjSh = pDoc->GetDocumentShell(); + + if (pViewSh && pViewSh->GetObjectShell() == pObjSh) + return pViewSh->GetWindow(); + + return NULL; +} + +BOOL ScDrawLayer::ScAddPage( SCTAB nTab ) +{ + if (bDrawIsInUndo) + return FALSE; // not inserted + + ScDrawPage* pPage = (ScDrawPage*)AllocPage( FALSE ); + InsertPage(pPage, static_cast<sal_uInt16>(nTab)); + if (bRecording) + AddCalcUndo(new SdrUndoNewPage(*pPage)); + + return TRUE; // inserted +} + +void ScDrawLayer::ScRemovePage( SCTAB nTab ) +{ + if (bDrawIsInUndo) + return; + + Broadcast( ScTabDeletedHint( nTab ) ); + if (bRecording) + { + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + AddCalcUndo(new SdrUndoDelPage(*pPage)); // Undo-Action wird Owner der Page + RemovePage( static_cast<sal_uInt16>(nTab) ); // nur austragen, nicht loeschen + } + else + DeletePage( static_cast<sal_uInt16>(nTab) ); // einfach weg damit +} + +void ScDrawLayer::ScRenamePage( SCTAB nTab, const String& rNewName ) +{ + ScDrawPage* pPage = (ScDrawPage*) GetPage(static_cast<sal_uInt16>(nTab)); + if (pPage) + pPage->SetName(rNewName); +} + +void ScDrawLayer::ScMovePage( USHORT nOldPos, USHORT nNewPos ) +{ + MovePage( nOldPos, nNewPos ); +} + +void ScDrawLayer::ScCopyPage( USHORT nOldPos, USHORT nNewPos, BOOL bAlloc ) +{ + //! remove argument bAlloc (always FALSE) + + if (bDrawIsInUndo) + return; + + SdrPage* pOldPage = GetPage(nOldPos); + SdrPage* pNewPage = bAlloc ? AllocPage(FALSE) : GetPage(nNewPos); + + // kopieren + + if (pOldPage && pNewPage) + { + SdrObjListIter aIter( *pOldPage, IM_FLAT ); + SdrObject* pOldObject = aIter.Next(); + while (pOldObject) + { + // #116235# + SdrObject* pNewObject = pOldObject->Clone(); + //SdrObject* pNewObject = pOldObject->Clone( pNewPage, this ); + pNewObject->SetModel(this); + pNewObject->SetPage(pNewPage); + + pNewObject->NbcMove(Size(0,0)); + pNewPage->InsertObject( pNewObject ); + if (bRecording) + AddCalcUndo( new SdrUndoInsertObj( *pNewObject ) ); + + pOldObject = aIter.Next(); + } + } + + if (bAlloc) + InsertPage(pNewPage, nNewPos); +} + +inline BOOL IsInBlock( const ScAddress& rPos, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2 ) +{ + return rPos.Col() >= nCol1 && rPos.Col() <= nCol2 && + rPos.Row() >= nRow1 && rPos.Row() <= nRow2; +} + +void ScDrawLayer::MoveCells( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, + SCsCOL nDx,SCsROW nDy ) +{ + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page nicht gefunden"); + if (!pPage) + return; + + BOOL bNegativePage = pDoc && pDoc->IsNegativePage( nTab ); + + ULONG nCount = pPage->GetObjCount(); + for ( ULONG i = 0; i < nCount; i++ ) + { + SdrObject* pObj = pPage->GetObj( i ); + ScDrawObjData* pData = GetObjDataTab( pObj, nTab ); + if( pData ) + { + const ScAddress aOldStt = pData->maStart; + const ScAddress aOldEnd = pData->maEnd; + BOOL bChange = FALSE; + if ( aOldStt.IsValid() && IsInBlock( aOldStt, nCol1,nRow1, nCol2,nRow2 ) ) + { + pData->maStart.IncCol( nDx ); + pData->maStart.IncRow( nDy ); + bChange = TRUE; + } + if ( aOldEnd.IsValid() && IsInBlock( aOldEnd, nCol1,nRow1, nCol2,nRow2 ) ) + { + pData->maEnd.IncCol( nDx ); + pData->maEnd.IncRow( nDy ); + bChange = TRUE; + } + if (bChange) + { + if ( pObj->ISA( SdrRectObj ) && pData->maStart.IsValid() && pData->maEnd.IsValid() ) + pData->maStart.PutInOrder( pData->maEnd ); + AddCalcUndo( new ScUndoObjData( pObj, aOldStt, aOldEnd, pData->maStart, pData->maEnd ) ); + RecalcPos( pObj, *pData, bNegativePage ); + } + } + } +} + +void ScDrawLayer::SetPageSize( USHORT nPageNo, const Size& rSize ) +{ + SdrPage* pPage = GetPage(nPageNo); + if (pPage) + { + if ( rSize != pPage->GetSize() ) + { + pPage->SetSize( rSize ); + Broadcast( ScTabSizeChangedHint( static_cast<SCTAB>(nPageNo) ) ); // SetWorkArea() an den Views + } + + // Detektivlinien umsetzen (an neue Hoehen/Breiten anpassen) + // auch wenn Groesse gleich geblieben ist + // (einzelne Zeilen/Spalten koennen geaendert sein) + + BOOL bNegativePage = pDoc && pDoc->IsNegativePage( static_cast<SCTAB>(nPageNo) ); + + ULONG nCount = pPage->GetObjCount(); + for ( ULONG i = 0; i < nCount; i++ ) + { + SdrObject* pObj = pPage->GetObj( i ); + ScDrawObjData* pData = GetObjDataTab( pObj, static_cast<SCTAB>(nPageNo) ); + if( pData ) + RecalcPos( pObj, *pData, bNegativePage ); + } + } +} + +void ScDrawLayer::RecalcPos( SdrObject* pObj, const ScDrawObjData& rData, bool bNegativePage ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::RecalcPos - missing document" ); + if( !pDoc ) + return; + + /* TODO CleanUp: Updating note position works just by chance currently... + When inserting rows/columns, this function is called after the + insertion, and the note is located at the new position contained in the + passed ScDrawObjData already. But when deleting rows/columns, this + function is called *before* the deletion, so the note is still at the + old cell position, and ScDocument::GetNote() will fail to get the note + or will get another note. But after the rows/columns are deleted, a + call to ScDrawLayer::SetPageSize() will call this function again, and + now the note is at the expected position in the document. */ + if( rData.mbNote ) + { + DBG_ASSERT( rData.maStart.IsValid(), "ScDrawLayer::RecalcPos - invalid position for cell note" ); + /* When inside an undo action, there may be pending note captions + where cell note is already deleted. The caption will be deleted + later with drawing undo. */ + if( ScPostIt* pNote = pDoc->GetNote( rData.maStart ) ) + pNote->UpdateCaptionPos( rData.maStart ); + return; + } + + bool bValid1 = rData.maStart.IsValid(); + SCCOL nCol1 = rData.maStart.Col(); + SCROW nRow1 = rData.maStart.Row(); + SCTAB nTab1 = rData.maStart.Tab(); + bool bValid2 = rData.maEnd.IsValid(); + SCCOL nCol2 = rData.maEnd.Col(); + SCROW nRow2 = rData.maEnd.Row(); + SCTAB nTab2 = rData.maEnd.Tab(); + + // validation circle + bool bCircle = pObj->ISA( SdrCircObj ); + // detective arrow + bool bArrow = pObj->IsPolyObj() && (pObj->GetPointCount() == 2); + + if( bCircle ) + { + Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); + TwipsToMM( aPos.X() ); + TwipsToMM( aPos.Y() ); + + // Berechnung und Werte wie in detfunc.cxx + + Size aSize( (long)(pDoc->GetColWidth( nCol1, nTab1 ) * HMM_PER_TWIPS), + (long)(pDoc->GetRowHeight( nRow1, nTab1 ) * HMM_PER_TWIPS) ); + Rectangle aRect( aPos, aSize ); + aRect.Left() -= 250; + aRect.Right() += 250; + aRect.Top() -= 70; + aRect.Bottom() += 70; + if ( bNegativePage ) + MirrorRectRTL( aRect ); + + if ( pObj->GetLogicRect() != aRect ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetLogicRect(aRect); + } + } + else if( bArrow ) + { + //! nicht mehrere Undos fuer ein Objekt erzeugen (hinteres kann dann weggelassen werden) + + if( bValid1 ) + { + Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); + if( (pDoc->GetColFlags( nCol1, nTab1 ) & CR_HIDDEN) == 0 ) + aPos.X() += pDoc->GetColWidth( nCol1, nTab1 ) / 4; + if( (pDoc->GetRowFlags( nRow1, nTab1 ) & CR_HIDDEN) == 0 ) + aPos.Y() += pDoc->GetRowHeight( nRow1, nTab1 ) / 2; + TwipsToMM( aPos.X() ); + TwipsToMM( aPos.Y() ); + Point aStartPos = aPos; + if ( bNegativePage ) + aStartPos.X() = -aStartPos.X(); // don't modify aPos - used below + if ( pObj->GetPoint( 0 ) != aStartPos ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetPoint( aStartPos, 0 ); + } + + if( !bValid2 ) + { + Point aEndPos( aPos.X() + DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); + if (aEndPos.Y() < 0) + aEndPos.Y() += (2 * DET_ARROW_OFFSET); + if ( bNegativePage ) + aEndPos.X() = -aEndPos.X(); + if ( pObj->GetPoint( 1 ) != aEndPos ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetPoint( aEndPos, 1 ); + } + } + } + if( bValid2 ) + { + Point aPos( pDoc->GetColOffset( nCol2, nTab2 ), pDoc->GetRowOffset( nRow2, nTab2 ) ); + if( (pDoc->GetColFlags( nCol2, nTab2 ) & CR_HIDDEN) == 0 ) + aPos.X() += pDoc->GetColWidth( nCol2, nTab2 ) / 4; + if( (pDoc->GetRowFlags( nRow2, nTab2 ) & CR_HIDDEN) == 0 ) + aPos.Y() += pDoc->GetRowHeight( nRow2, nTab2 ) / 2; + TwipsToMM( aPos.X() ); + TwipsToMM( aPos.Y() ); + Point aEndPos = aPos; + if ( bNegativePage ) + aEndPos.X() = -aEndPos.X(); // don't modify aPos - used below + if ( pObj->GetPoint( 1 ) != aEndPos ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetPoint( aEndPos, 1 ); + } + + if( !bValid1 ) + { + Point aStartPos( aPos.X() - DET_ARROW_OFFSET, aPos.Y() - DET_ARROW_OFFSET ); + if (aStartPos.X() < 0) + aStartPos.X() += (2 * DET_ARROW_OFFSET); + if (aStartPos.Y() < 0) + aStartPos.Y() += (2 * DET_ARROW_OFFSET); + if ( bNegativePage ) + aStartPos.X() = -aStartPos.X(); + if ( pObj->GetPoint( 0 ) != aStartPos ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetPoint( aStartPos, 0 ); + } + } + } + } + else // Referenz-Rahmen + { + DBG_ASSERT( bValid1, "ScDrawLayer::RecalcPos - invalid start position" ); + Point aPos( pDoc->GetColOffset( nCol1, nTab1 ), pDoc->GetRowOffset( nRow1, nTab1 ) ); + TwipsToMM( aPos.X() ); + TwipsToMM( aPos.Y() ); + + if( bValid2 ) + { + Point aEnd( pDoc->GetColOffset( nCol2 + 1, nTab2 ), pDoc->GetRowOffset( nRow2 + 1, nTab2 ) ); + TwipsToMM( aEnd.X() ); + TwipsToMM( aEnd.Y() ); + + Rectangle aNew( aPos, aEnd ); + if ( bNegativePage ) + MirrorRectRTL( aNew ); + if ( pObj->GetLogicRect() != aNew ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetLogicRect(aNew); + } + } + else + { + if ( bNegativePage ) + aPos.X() = -aPos.X(); + if ( pObj->GetRelativePos() != aPos ) + { + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->SetRelativePos( aPos ); + } + } + } +} + +BOOL ScDrawLayer::GetPrintArea( ScRange& rRange, BOOL bSetHor, BOOL bSetVer ) const +{ + DBG_ASSERT( pDoc, "ScDrawLayer::GetPrintArea without document" ); + if ( !pDoc ) + return FALSE; + + SCTAB nTab = rRange.aStart.Tab(); + DBG_ASSERT( rRange.aEnd.Tab() == nTab, "GetPrintArea: Tab unterschiedlich" ); + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + + BOOL bAny = FALSE; + long nEndX = 0; + long nEndY = 0; + long nStartX = LONG_MAX; + long nStartY = LONG_MAX; + + // Grenzen ausrechnen + + if (!bSetHor) + { + nStartX = 0; + SCCOL nStartCol = rRange.aStart.Col(); + SCCOL i; + for (i=0; i<nStartCol; i++) + nStartX +=pDoc->GetColWidth(i,nTab); + nEndX = nStartX; + SCCOL nEndCol = rRange.aEnd.Col(); + for (i=nStartCol; i<=nEndCol; i++) + nEndX += pDoc->GetColWidth(i,nTab); + nStartX = (long)(nStartX * HMM_PER_TWIPS); + nEndX = (long)(nEndX * HMM_PER_TWIPS); + } + if (!bSetVer) + { + nStartY = pDoc->FastGetRowHeight( 0, rRange.aStart.Row()-1, nTab); + nEndY = nStartY + pDoc->FastGetRowHeight( rRange.aStart.Row(), + rRange.aEnd.Row(), nTab); + nStartY = (long)(nStartY * HMM_PER_TWIPS); + nEndY = (long)(nEndY * HMM_PER_TWIPS); + } + + if ( bNegativePage ) + { + nStartX = -nStartX; // positions are negative, swap start/end so the same comparisons work + nEndX = -nEndX; + ::std::swap( nStartX, nEndX ); + } + + const SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page nicht gefunden"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + //! Flags (ausgeblendet?) testen + + Rectangle aObjRect = pObject->GetCurrentBoundRect(); + BOOL bFit = TRUE; + if ( !bSetHor && ( aObjRect.Right() < nStartX || aObjRect.Left() > nEndX ) ) + bFit = FALSE; + if ( !bSetVer && ( aObjRect.Bottom() < nStartY || aObjRect.Top() > nEndY ) ) + bFit = FALSE; + if ( bFit ) + { + if (bSetHor) + { + if (aObjRect.Left() < nStartX) nStartX = aObjRect.Left(); + if (aObjRect.Right() > nEndX) nEndX = aObjRect.Right(); + } + if (bSetVer) + { + if (aObjRect.Top() < nStartY) nStartY = aObjRect.Top(); + if (aObjRect.Bottom() > nEndY) nEndY = aObjRect.Bottom(); + } + bAny = TRUE; + } + + pObject = aIter.Next(); + } + } + + if ( bNegativePage ) + { + nStartX = -nStartX; // reverse transformation, so the same cell address calculation works + nEndX = -nEndX; + ::std::swap( nStartX, nEndX ); + } + + if (bAny) + { + DBG_ASSERT( nStartX<=nEndX && nStartY<=nEndY, "Start/End falsch in ScDrawLayer::GetPrintArea" ); + + if (bSetHor) + { + nStartX = (long) (nStartX / HMM_PER_TWIPS); + nEndX = (long) (nEndX / HMM_PER_TWIPS); + long nWidth; + SCCOL i; + + nWidth = 0; + for (i=0; i<=MAXCOL && nWidth<=nStartX; i++) + nWidth += pDoc->GetColWidth(i,nTab); + rRange.aStart.SetCol( i>0 ? (i-1) : 0 ); + + nWidth = 0; + for (i=0; i<=MAXCOL && nWidth<=nEndX; i++) //! bei Start anfangen + nWidth += pDoc->GetColWidth(i,nTab); + rRange.aEnd.SetCol( i>0 ? (i-1) : 0 ); + } + + if (bSetVer) + { + nStartY = (long) (nStartY / HMM_PER_TWIPS); + nEndY = (long) (nEndY / HMM_PER_TWIPS); + SCROW nRow = pDoc->FastGetRowForHeight( nTab, nStartY); + rRange.aStart.SetRow( nRow>0 ? (nRow-1) : 0); + nRow = pDoc->FastGetRowForHeight( nTab, nEndY); + rRange.aEnd.SetRow( nRow == MAXROW ? MAXROW : + (nRow>0 ? (nRow-1) : 0)); + } + } + else + { + if (bSetHor) + { + rRange.aStart.SetCol(0); + rRange.aEnd.SetCol(0); + } + if (bSetVer) + { + rRange.aStart.SetRow(0); + rRange.aEnd.SetRow(0); + } + } + return bAny; +} + +void ScDrawLayer::AddCalcUndo( SdrUndoAction* pUndo ) +{ + if (bRecording) + { + if (!pUndoGroup) + pUndoGroup = new SdrUndoGroup(*this); + + pUndoGroup->AddAction( pUndo ); + } + else + delete pUndo; +} + +void ScDrawLayer::BeginCalcUndo() +{ +//! DBG_ASSERT( !bRecording, "BeginCalcUndo ohne GetCalcUndo" ); + + DELETEZ(pUndoGroup); + bRecording = TRUE; +} + +SdrUndoGroup* ScDrawLayer::GetCalcUndo() +{ +//! DBG_ASSERT( bRecording, "GetCalcUndo ohne BeginCalcUndo" ); + + SdrUndoGroup* pRet = pUndoGroup; + pUndoGroup = NULL; + bRecording = FALSE; + return pRet; +} + +// MoveAreaTwips: all measures are kept in twips +void ScDrawLayer::MoveAreaTwips( SCTAB nTab, const Rectangle& rArea, + const Point& rMove, const Point& rTopLeft ) +{ + if (!rMove.X() && !rMove.Y()) + return; // nix + + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page nicht gefunden"); + if (!pPage) + return; + + BOOL bNegativePage = pDoc && pDoc->IsNegativePage( nTab ); + + // fuer Shrinking! + Rectangle aNew( rArea ); + BOOL bShrink = FALSE; + if ( rMove.X() < 0 || rMove.Y() < 0 ) // verkleinern + { + if ( rTopLeft != rArea.TopLeft() ) // sind gleich beim Verschieben von Zellen + { + bShrink = TRUE; + aNew.Left() = rTopLeft.X(); + aNew.Top() = rTopLeft.Y(); + } + } + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if( GetAnchor( pObject ) == SCA_CELL ) + { + if ( GetObjData( pObject ) ) // Detektiv-Pfeil ? + { + // hier nichts + } + else if ( pObject->ISA( SdrEdgeObj ) ) // Verbinder? + { + // hier auch nichts + //! nicht verbundene Enden wie bei Linien (s.u.) behandeln? + } + else if ( pObject->IsPolyObj() && pObject->GetPointCount()==2 ) + { + for (USHORT i=0; i<2; i++) + { + BOOL bMoved = FALSE; + Point aPoint = pObject->GetPoint(i); + lcl_ReverseTwipsToMM( aPoint ); + if (rArea.IsInside(aPoint)) + { + aPoint += rMove; bMoved = TRUE; + } + else if (bShrink && aNew.IsInside(aPoint)) + { + // Punkt ist in betroffener Zelle - Test auf geloeschten Bereich + if ( rMove.X() && aPoint.X() >= rArea.Left() + rMove.X() ) + { + aPoint.X() = rArea.Left() + rMove.X() - SHRINK_DIST_TWIPS; + if ( aPoint.X() < 0 ) aPoint.X() = 0; + bMoved = TRUE; + } + if ( rMove.Y() && aPoint.Y() >= rArea.Top() + rMove.Y() ) + { + aPoint.Y() = rArea.Top() + rMove.Y() - SHRINK_DIST_TWIPS; + if ( aPoint.Y() < 0 ) aPoint.Y() = 0; + bMoved = TRUE; + } + } + if( bMoved ) + { + AddCalcUndo( new SdrUndoGeoObj( *pObject ) ); + lcl_TwipsToMM( aPoint ); + pObject->SetPoint( aPoint, i ); + } + } + } + else + { + Rectangle aObjRect = pObject->GetLogicRect(); + // aOldMMPos: not converted, millimeters + Point aOldMMPos = bNegativePage ? aObjRect.TopRight() : aObjRect.TopLeft(); + lcl_ReverseTwipsToMM( aObjRect ); + Point aTopLeft = bNegativePage ? aObjRect.TopRight() : aObjRect.TopLeft(); // logical left + Size aMoveSize; + BOOL bDoMove = FALSE; + if (rArea.IsInside(aTopLeft)) + { + aMoveSize = Size(rMove.X(),rMove.Y()); + bDoMove = TRUE; + } + else if (bShrink && aNew.IsInside(aTopLeft)) + { + // Position ist in betroffener Zelle - Test auf geloeschten Bereich + if ( rMove.X() && aTopLeft.X() >= rArea.Left() + rMove.X() ) + { + aMoveSize.Width() = rArea.Left() + rMove.X() - SHRINK_DIST - aTopLeft.X(); + bDoMove = TRUE; + } + if ( rMove.Y() && aTopLeft.Y() >= rArea.Top() + rMove.Y() ) + { + aMoveSize.Height() = rArea.Top() + rMove.Y() - SHRINK_DIST - aTopLeft.Y(); + bDoMove = TRUE; + } + } + if ( bDoMove ) + { + if ( bNegativePage ) + { + if ( aTopLeft.X() + aMoveSize.Width() > 0 ) + aMoveSize.Width() = -aTopLeft.X(); + } + else + { + if ( aTopLeft.X() + aMoveSize.Width() < 0 ) + aMoveSize.Width() = -aTopLeft.X(); + } + if ( aTopLeft.Y() + aMoveSize.Height() < 0 ) + aMoveSize.Height() = -aTopLeft.Y(); + + // get corresponding move size in millimeters: + Point aNewPos( aTopLeft.X() + aMoveSize.Width(), aTopLeft.Y() + aMoveSize.Height() ); + lcl_TwipsToMM( aNewPos ); + aMoveSize = Size( aNewPos.X() - aOldMMPos.X(), aNewPos.Y() - aOldMMPos.Y() ); // millimeters + + AddCalcUndo( new SdrUndoMoveObj( *pObject, aMoveSize ) ); + pObject->Move( aMoveSize ); + } + else if ( rArea.IsInside( bNegativePage ? aObjRect.BottomLeft() : aObjRect.BottomRight() ) && + !pObject->IsResizeProtect() ) + { + // geschuetzte Groessen werden nicht veraendert + // (Positionen schon, weil sie ja an der Zelle "verankert" sind) + AddCalcUndo( new SdrUndoGeoObj( *pObject ) ); + long nOldSizeX = aObjRect.Right() - aObjRect.Left() + 1; + long nOldSizeY = aObjRect.Bottom() - aObjRect.Top() + 1; + long nLogMoveX = rMove.X() * ( bNegativePage ? -1 : 1 ); // logical direction + pObject->Resize( aOldMMPos, Fraction( nOldSizeX+nLogMoveX, nOldSizeX ), + Fraction( nOldSizeY+rMove.Y(), nOldSizeY ) ); + } + } + } + pObject = aIter.Next(); + } +} + +void ScDrawLayer::MoveArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, SCCOL nCol2,SCROW nRow2, + SCsCOL nDx,SCsROW nDy, BOOL bInsDel ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::MoveArea without document" ); + if ( !pDoc ) + return; + + if (!bAdjustEnabled) + return; + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + + Rectangle aRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); + lcl_ReverseTwipsToMM( aRect ); + //! use twips directly? + + Point aMove; + + if (nDx > 0) + for (SCsCOL s=0; s<nDx; s++) + aMove.X() += pDoc->GetColWidth(s+(SCsCOL)nCol1,nTab); + else + for (SCsCOL s=-1; s>=nDx; s--) + aMove.X() -= pDoc->GetColWidth(s+(SCsCOL)nCol1,nTab); + if (nDy > 0) + aMove.Y() += pDoc->FastGetRowHeight( nRow1, nRow1+nDy-1, nTab); + else + aMove.Y() -= pDoc->FastGetRowHeight( nRow1+nDy, nRow1-1, nTab); + + if ( bNegativePage ) + aMove.X() = -aMove.X(); + + Point aTopLeft = aRect.TopLeft(); // Anfang beim Verkleinern + if (bInsDel) + { + if ( aMove.X() != 0 && nDx < 0 ) // nDx counts cells, sign is independent of RTL + aTopLeft.X() += aMove.X(); + if ( aMove.Y() < 0 ) + aTopLeft.Y() += aMove.Y(); + } + + // drawing objects are now directly included in cut&paste + // -> only update references when inserting/deleting (or changing widths or heights) + if ( bInsDel ) + MoveAreaTwips( nTab, aRect, aMove, aTopLeft ); + + // + // Detektiv-Pfeile: Zellpositionen anpassen + // + + MoveCells( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy ); +} + +void ScDrawLayer::WidthChanged( SCTAB nTab, SCCOL nCol, long nDifTwips ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::WidthChanged without document" ); + if ( !pDoc ) + return; + + if (!bAdjustEnabled) + return; + + Rectangle aRect; + Point aTopLeft; + + for (SCCOL i=0; i<nCol; i++) + aRect.Left() += pDoc->GetColWidth(i,nTab); + aTopLeft.X() = aRect.Left(); + aRect.Left() += pDoc->GetColWidth(nCol,nTab); + + aRect.Right() = MAXMM; + aRect.Top() = 0; + aRect.Bottom() = MAXMM; + + //! aTopLeft ist falsch, wenn mehrere Spalten auf einmal ausgeblendet werden + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + if ( bNegativePage ) + { + MirrorRectRTL( aRect ); + aTopLeft.X() = -aTopLeft.X(); + nDifTwips = -nDifTwips; + } + + MoveAreaTwips( nTab, aRect, Point( nDifTwips,0 ), aTopLeft ); +} + +void ScDrawLayer::HeightChanged( SCTAB nTab, SCROW nRow, long nDifTwips ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::HeightChanged without document" ); + if ( !pDoc ) + return; + + if (!bAdjustEnabled) + return; + + Rectangle aRect; + Point aTopLeft; + + aRect.Top() += pDoc->FastGetRowHeight( 0, nRow-1, nTab); + aTopLeft.Y() = aRect.Top(); + aRect.Top() += pDoc->FastGetRowHeight(nRow,nTab); + + aRect.Bottom() = MAXMM; + aRect.Left() = 0; + aRect.Right() = MAXMM; + + //! aTopLeft ist falsch, wenn mehrere Zeilen auf einmal ausgeblendet werden + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + if ( bNegativePage ) + { + MirrorRectRTL( aRect ); + aTopLeft.X() = -aTopLeft.X(); + } + + MoveAreaTwips( nTab, aRect, Point( 0,nDifTwips ), aTopLeft ); +} + +BOOL ScDrawLayer::HasObjectsInRows( SCTAB nTab, SCROW nStartRow, SCROW nEndRow ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::HasObjectsInRows without document" ); + if ( !pDoc ) + return FALSE; + + Rectangle aTestRect; + + aTestRect.Top() += pDoc->FastGetRowHeight( 0, nStartRow-1, nTab); + + if (nEndRow==MAXROW) + aTestRect.Bottom() = MAXMM; + else + { + aTestRect.Bottom() = aTestRect.Top(); + aTestRect.Bottom() += pDoc->FastGetRowHeight( nStartRow, nEndRow, nTab); + TwipsToMM( aTestRect.Bottom() ); + } + + TwipsToMM( aTestRect.Top() ); + + aTestRect.Left() = 0; + aTestRect.Right() = MAXMM; + + BOOL bNegativePage = pDoc->IsNegativePage( nTab ); + if ( bNegativePage ) + MirrorRectRTL( aTestRect ); + + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page nicht gefunden"); + if (!pPage) + return FALSE; + + BOOL bFound = FALSE; + + Rectangle aObjRect; + SdrObjListIter aIter( *pPage ); + SdrObject* pObject = aIter.Next(); + while ( pObject && !bFound ) + { + aObjRect = pObject->GetSnapRect(); //! GetLogicRect ? + if (aTestRect.IsInside(aObjRect.TopLeft()) || aTestRect.IsInside(aObjRect.BottomLeft())) + bFound = TRUE; + + pObject = aIter.Next(); + } + + return bFound; +} + +#if 0 +void ScDrawLayer::DeleteObjects( SCTAB nTab ) +{ + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) + return; + + pPage->RecalcObjOrdNums(); + + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + // alle loeschen + ppObj[nDelCount++] = pObject; + pObject = aIter.Next(); + } + + long i; + if (bRecording) + for (i=1; i<=nDelCount; i++) + AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + } +} +#endif + +void ScDrawLayer::DeleteObjectsInArea( SCTAB nTab, SCCOL nCol1,SCROW nRow1, + SCCOL nCol2,SCROW nRow2 ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::DeleteObjectsInArea without document" ); + if ( !pDoc ) + return; + + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + DBG_ASSERT(pPage,"Page ?"); + if (!pPage) + return; + + pPage->RecalcObjOrdNums(); + + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + Rectangle aDelRect = pDoc->GetMMRect( nCol1, nRow1, nCol2, nRow2, nTab ); + + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + // do not delete note caption, they are always handled by the cell note + // TODO: detective objects are still deleted, is this desired? + if (!IsNoteCaption( pObject )) + { + Rectangle aObjRect = pObject->GetCurrentBoundRect(); + if ( aDelRect.IsInside( aObjRect ) ) + ppObj[nDelCount++] = pObject; + } + + pObject = aIter.Next(); + } + + long i; + if (bRecording) + for (i=1; i<=nDelCount; i++) + AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + } +} + +void ScDrawLayer::DeleteObjectsInSelection( const ScMarkData& rMark ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::DeleteObjectsInSelection without document" ); + if ( !pDoc ) + return; + + if ( !rMark.IsMultiMarked() ) + return; + + ScRange aMarkRange; + rMark.GetMultiMarkArea( aMarkRange ); + + SCTAB nTabCount = pDoc->GetTableCount(); + for (SCTAB nTab=0; nTab<=nTabCount; nTab++) + if ( rMark.GetTableSelect( nTab ) ) + { + SdrPage* pPage = GetPage(static_cast<sal_uInt16>(nTab)); + if (pPage) + { + pPage->RecalcObjOrdNums(); + long nDelCount = 0; + ULONG nObjCount = pPage->GetObjCount(); + if (nObjCount) + { + // Rechteck um die ganze Selektion + Rectangle aMarkBound = pDoc->GetMMRect( + aMarkRange.aStart.Col(), aMarkRange.aStart.Row(), + aMarkRange.aEnd.Col(), aMarkRange.aEnd.Row(), nTab ); + + SdrObject** ppObj = new SdrObject*[nObjCount]; + + SdrObjListIter aIter( *pPage, IM_FLAT ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + // do not delete note caption, they are always handled by the cell note + // TODO: detective objects are still deleted, is this desired? + if (!IsNoteCaption( pObject )) + { + Rectangle aObjRect = pObject->GetCurrentBoundRect(); + if ( aMarkBound.IsInside( aObjRect ) ) + { + ScRange aRange = pDoc->GetRange( nTab, aObjRect ); + if (rMark.IsAllMarked(aRange)) + ppObj[nDelCount++] = pObject; + } + } + + pObject = aIter.Next(); + } + + // Objekte loeschen (rueckwaerts) + + long i; + if (bRecording) + for (i=1; i<=nDelCount; i++) + AddCalcUndo( new SdrUndoRemoveObj( *ppObj[nDelCount-i] ) ); + + for (i=1; i<=nDelCount; i++) + pPage->RemoveObject( ppObj[nDelCount-i]->GetOrdNum() ); + + delete[] ppObj; + } + } + else + { + DBG_ERROR("pPage?"); + } + } +} + +void ScDrawLayer::CopyToClip( ScDocument* pClipDoc, SCTAB nTab, const Rectangle& rRange ) +{ + // copy everything in the specified range into the same page (sheet) in the clipboard doc + + SdrPage* pSrcPage = GetPage(static_cast<sal_uInt16>(nTab)); + if (pSrcPage) + { + ScDrawLayer* pDestModel = NULL; + SdrPage* pDestPage = NULL; + + SdrObjListIter aIter( *pSrcPage, IM_FLAT ); + SdrObject* pOldObject = aIter.Next(); + while (pOldObject) + { + Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); + // do not copy internal objects (detective) and note captions + if ( rRange.IsInside( aObjRect ) && (pOldObject->GetLayer() != SC_LAYER_INTERN) && !IsNoteCaption( pOldObject ) ) + { + if ( !pDestModel ) + { + pDestModel = pClipDoc->GetDrawLayer(); // does the document already have a drawing layer? + if ( !pDestModel ) + { + // allocate drawing layer in clipboard document only if there are objects to copy + + pClipDoc->InitDrawLayer(); //! create contiguous pages + pDestModel = pClipDoc->GetDrawLayer(); + } + if (pDestModel) + pDestPage = pDestModel->GetPage( static_cast<sal_uInt16>(nTab) ); + } + + DBG_ASSERT( pDestPage, "no page" ); + if (pDestPage) + { + // #116235# + SdrObject* pNewObject = pOldObject->Clone(); + //SdrObject* pNewObject = pOldObject->Clone( pDestPage, pDestModel ); + pNewObject->SetModel(pDestModel); + pNewObject->SetPage(pDestPage); + + pNewObject->NbcMove(Size(0,0)); + pDestPage->InsertObject( pNewObject ); + + // no undo needed in clipboard document + // charts are not updated + } + } + + pOldObject = aIter.Next(); + } + } +} + +BOOL lcl_IsAllInRange( const ScRangeList& rRanges, const ScRange& rClipRange ) +{ + // check if every range of rRanges is completely in rClipRange + + ULONG nCount = rRanges.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange aRange = *rRanges.GetObject(i); + if ( !rClipRange.In( aRange ) ) + { + return FALSE; // at least one range is not valid + } + } + + return TRUE; // everything is fine +} + +BOOL lcl_MoveRanges( ScRangeList& rRanges, const ScRange& rSourceRange, const ScAddress& rDestPos ) +{ + BOOL bChanged = FALSE; + + ULONG nCount = rRanges.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange* pRange = rRanges.GetObject(i); + if ( rSourceRange.In( *pRange ) ) + { + SCsCOL nDiffX = rDestPos.Col() - (SCsCOL)rSourceRange.aStart.Col(); + SCsROW nDiffY = rDestPos.Row() - (SCsROW)rSourceRange.aStart.Row(); + SCsTAB nDiffZ = rDestPos.Tab() - (SCsTAB)rSourceRange.aStart.Tab(); + pRange->Move( nDiffX, nDiffY, nDiffZ ); + bChanged = TRUE; + } + } + + return bChanged; +} + +void ScDrawLayer::CopyFromClip( ScDrawLayer* pClipModel, SCTAB nSourceTab, const Rectangle& rSourceRange, + const ScAddress& rDestPos, const Rectangle& rDestRange ) +{ + DBG_ASSERT( pDoc, "ScDrawLayer::CopyFromClip without document" ); + if ( !pDoc ) + return; + + if (!pClipModel) + return; + + if (bDrawIsInUndo) //! can this happen? + { + DBG_ERROR("CopyFromClip, bDrawIsInUndo"); + return; + } + + BOOL bMirrorObj = ( rSourceRange.Left() < 0 && rSourceRange.Right() < 0 && + rDestRange.Left() > 0 && rDestRange.Right() > 0 ) || + ( rSourceRange.Left() > 0 && rSourceRange.Right() > 0 && + rDestRange.Left() < 0 && rDestRange.Right() < 0 ); + Rectangle aMirroredSource = rSourceRange; + if ( bMirrorObj ) + MirrorRectRTL( aMirroredSource ); + + SCTAB nDestTab = rDestPos.Tab(); + + SdrPage* pSrcPage = pClipModel->GetPage(static_cast<sal_uInt16>(nSourceTab)); + SdrPage* pDestPage = GetPage(static_cast<sal_uInt16>(nDestTab)); + DBG_ASSERT( pSrcPage && pDestPage, "draw page missing" ); + if ( !pSrcPage || !pDestPage ) + return; + + // first mirror, then move + Size aMove( rDestRange.Left() - aMirroredSource.Left(), rDestRange.Top() - aMirroredSource.Top() ); + + long nDestWidth = rDestRange.GetWidth(); + long nDestHeight = rDestRange.GetHeight(); + long nSourceWidth = rSourceRange.GetWidth(); + long nSourceHeight = rSourceRange.GetHeight(); + + long nWidthDiff = nDestWidth - nSourceWidth; + long nHeightDiff = nDestHeight - nSourceHeight; + + Fraction aHorFract(1,1); + Fraction aVerFract(1,1); + BOOL bResize = FALSE; + // sizes can differ by 1 from twips->1/100mm conversion for equal cell sizes, + // don't resize to empty size when pasting into hidden columns or rows + if ( Abs(nWidthDiff) > 1 && nDestWidth > 1 && nSourceWidth > 1 ) + { + aHorFract = Fraction( nDestWidth, nSourceWidth ); + bResize = TRUE; + } + if ( Abs(nHeightDiff) > 1 && nDestHeight > 1 && nSourceHeight > 1 ) + { + aVerFract = Fraction( nDestHeight, nSourceHeight ); + bResize = TRUE; + } + Point aRefPos = rDestRange.TopLeft(); // for resizing (after moving) + + SdrObjListIter aIter( *pSrcPage, IM_FLAT ); + SdrObject* pOldObject = aIter.Next(); + while (pOldObject) + { + Rectangle aObjRect = pOldObject->GetCurrentBoundRect(); + // do not copy internal objects (detective) and note captions + if ( rSourceRange.IsInside( aObjRect ) && (pOldObject->GetLayer() != SC_LAYER_INTERN) && !IsNoteCaption( pOldObject ) ) + { + // #116235# + SdrObject* pNewObject = pOldObject->Clone(); + //SdrObject* pNewObject = pOldObject->Clone( pDestPage, this ); + pNewObject->SetModel(this); + pNewObject->SetPage(pDestPage); + + if ( bMirrorObj ) + MirrorRTL( pNewObject ); // first mirror, then move + + pNewObject->NbcMove( aMove ); + if ( bResize ) + pNewObject->NbcResize( aRefPos, aHorFract, aVerFract ); + + pDestPage->InsertObject( pNewObject ); + if (bRecording) + AddCalcUndo( new SdrUndoInsertObj( *pNewObject ) ); + + // handle chart data references (after InsertObject) + + if ( pNewObject->GetObjIdentifier() == OBJ_OLE2 ) + { + uno::Reference< embed::XEmbeddedObject > xIPObj = ((SdrOle2Obj*)pNewObject)->GetObjRef(); + uno::Reference< embed::XClassifiedObject > xClassified( xIPObj, uno::UNO_QUERY ); + SvGlobalName aObjectClassName; + if ( xClassified.is() ) + { + try { + aObjectClassName = SvGlobalName( xClassified->getClassID() ); + } catch( uno::Exception& ) + { + // TODO: handle error? + } + } + + if ( xIPObj.is() && SotExchange::IsChart( aObjectClassName ) ) + { + String aNewName = ((SdrOle2Obj*)pNewObject)->GetPersistName(); + + //! need to set new DataProvider, or does Chart handle this itself? + + ScRangeListRef xRanges( new ScRangeList ); + BOOL bColHeaders = FALSE; + BOOL bRowHeaders = FALSE; + pDoc->GetOldChartParameters( aNewName, *xRanges, bColHeaders, bRowHeaders ); + + if ( xRanges->Count() > 0 ) + { + ScDocument* pClipDoc = pClipModel->GetDocument(); + + // a clipboard document and its source share the same document item pool, + // so the pointers can be compared to see if this is copy&paste within + // the same document + BOOL bSameDoc = pDoc && pClipDoc && pDoc->GetPool() == pClipDoc->GetPool(); + + BOOL bDestClip = pDoc && pDoc->IsClipboard(); + + BOOL bInSourceRange = FALSE; + ScRange aClipRange; + if ( pClipDoc ) + { + SCCOL nClipStartX; + SCROW nClipStartY; + SCCOL nClipEndX; + SCROW nClipEndY; + pClipDoc->GetClipStart( nClipStartX, nClipStartY ); + pClipDoc->GetClipArea( nClipEndX, nClipEndY, TRUE ); + nClipEndX = nClipEndX + nClipStartX; + nClipEndY += nClipStartY; // GetClipArea returns the difference + + aClipRange = ScRange( nClipStartX, nClipStartY, nSourceTab, + nClipEndX, nClipEndY, nSourceTab ); + + bInSourceRange = lcl_IsAllInRange( *xRanges, aClipRange ); + } + + // always lose references when pasting into a clipboard document (transpose) + if ( ( bInSourceRange || bSameDoc ) && !bDestClip ) + { + if ( bInSourceRange ) + { + if ( rDestPos != aClipRange.aStart ) + { + // update the data ranges to the new (copied) position + ScRangeListRef xNewRanges = new ScRangeList( *xRanges ); + if ( lcl_MoveRanges( *xNewRanges, aClipRange, rDestPos ) ) + { + pDoc->UpdateChartArea( aNewName, xNewRanges, bColHeaders, bRowHeaders, FALSE ); + } + } + } + else + { + // leave the ranges unchanged + } + } + else + { + // pasting into a new document without the complete source data + // -> break connection to source data + + // (see ScDocument::UpdateChartListenerCollection, PastingDrawFromOtherDoc) + + //! need chart interface to switch to own data + } + } + } + } + } + + pOldObject = aIter.Next(); + } +} + +void ScDrawLayer::MirrorRTL( SdrObject* pObj ) +{ + UINT16 nIdent = pObj->GetObjIdentifier(); + + // don't mirror OLE or graphics, otherwise ask the object + // if it can be mirrored + BOOL bCanMirror = ( nIdent != OBJ_GRAF && nIdent != OBJ_OLE2 ); + if (bCanMirror) + { + SdrObjTransformInfoRec aInfo; + pObj->TakeObjInfo( aInfo ); + bCanMirror = aInfo.bMirror90Allowed; + } + + if (bCanMirror) + { + Point aRef1( 0, 0 ); + Point aRef2( 0, 1 ); + if (bRecording) + AddCalcUndo( new SdrUndoGeoObj( *pObj ) ); + pObj->Mirror( aRef1, aRef2 ); + } + else + { + // Move instead of mirroring: + // New start position is negative of old end position + // -> move by sum of start and end position + Rectangle aObjRect = pObj->GetLogicRect(); + Size aMoveSize( -(aObjRect.Left() + aObjRect.Right()), 0 ); + if (bRecording) + AddCalcUndo( new SdrUndoMoveObj( *pObj, aMoveSize ) ); + pObj->Move( aMoveSize ); + } +} + +// static +void ScDrawLayer::MirrorRectRTL( Rectangle& rRect ) +{ + // mirror and swap left/right + long nTemp = rRect.Left(); + rRect.Left() = -rRect.Right(); + rRect.Right() = -nTemp; +} + +Rectangle ScDrawLayer::GetCellRect( ScDocument& rDoc, const ScAddress& rPos, bool bMergedCell ) +{ + Rectangle aCellRect; + DBG_ASSERT( ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ), "ScDrawLayer::GetCellRect - invalid cell address" ); + if( ValidColRowTab( rPos.Col(), rPos.Row(), rPos.Tab() ) ) + { + // find top left position of passed cell address + Point aTopLeft; + for( SCCOL nCol = 0; nCol < rPos.Col(); ++nCol ) + aTopLeft.X() += rDoc.GetColWidth( nCol, rPos.Tab() ); + if( rPos.Row() > 0 ) + aTopLeft.Y() += rDoc.FastGetRowHeight( 0, rPos.Row() - 1, rPos.Tab() ); + + // find bottom-right position of passed cell address + ScAddress aEndPos = rPos; + if( bMergedCell ) + { + const ScMergeAttr* pMerge = static_cast< const ScMergeAttr* >( rDoc.GetAttr( rPos.Col(), rPos.Row(), rPos.Tab(), ATTR_MERGE ) ); + if( pMerge->GetColMerge() > 1 ) + aEndPos.IncCol( pMerge->GetColMerge() - 1 ); + if( pMerge->GetRowMerge() > 1 ) + aEndPos.IncRow( pMerge->GetRowMerge() - 1 ); + } + Point aBotRight = aTopLeft; + for( SCCOL nCol = rPos.Col(); nCol <= aEndPos.Col(); ++nCol ) + aBotRight.X() += rDoc.GetColWidth( nCol, rPos.Tab() ); + aBotRight.Y() += rDoc.FastGetRowHeight( rPos.Row(), aEndPos.Row(), rPos.Tab() ); + + // twips -> 1/100 mm + aTopLeft.X() = static_cast< long >( aTopLeft.X() * HMM_PER_TWIPS ); + aTopLeft.Y() = static_cast< long >( aTopLeft.Y() * HMM_PER_TWIPS ); + aBotRight.X() = static_cast< long >( aBotRight.X() * HMM_PER_TWIPS ); + aBotRight.Y() = static_cast< long >( aBotRight.Y() * HMM_PER_TWIPS ); + + aCellRect = Rectangle( aTopLeft, aBotRight ); + if( rDoc.IsNegativePage( rPos.Tab() ) ) + MirrorRectRTL( aCellRect ); + } + return aCellRect; +} + +// static +String ScDrawLayer::GetVisibleName( SdrObject* pObj ) +{ + String aName = pObj->GetName(); + if ( pObj->GetObjIdentifier() == OBJ_OLE2 ) + { + // #95575# For OLE, the user defined name (GetName) is used + // if it's not empty (accepting possibly duplicate names), + // otherwise the persist name is used so every object appears + // in the Navigator at all. + + if ( !aName.Len() ) + aName = static_cast<SdrOle2Obj*>(pObj)->GetPersistName(); + } + return aName; +} + +inline sal_Bool IsNamedObject( SdrObject* pObj, const String& rName ) +{ + // TRUE if rName is the object's Name or PersistName + // (used to find a named object) + + return ( pObj->GetName() == rName || + ( pObj->GetObjIdentifier() == OBJ_OLE2 && + static_cast<SdrOle2Obj*>(pObj)->GetPersistName() == rName ) ); +} + +SdrObject* ScDrawLayer::GetNamedObject( const String& rName, USHORT nId, SCTAB& rFoundTab ) const +{ + sal_uInt16 nTabCount = GetPageCount(); + for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) + { + const SdrPage* pPage = GetPage(nTab); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPWITHGROUPS ); + SdrObject* pObject = aIter.Next(); + while (pObject) + { + if ( nId == 0 || pObject->GetObjIdentifier() == nId ) + if ( IsNamedObject( pObject, rName ) ) + { + rFoundTab = static_cast<SCTAB>(nTab); + return pObject; + } + + pObject = aIter.Next(); + } + } + } + + return NULL; +} + +String ScDrawLayer::GetNewGraphicName( long* pnCounter ) const +{ + String aBase = ScGlobal::GetRscString(STR_GRAPHICNAME); + aBase += ' '; + + BOOL bThere = TRUE; + String aGraphicName; + SCTAB nDummy; + long nId = pnCounter ? *pnCounter : 0; + while (bThere) + { + ++nId; + aGraphicName = aBase; + aGraphicName += String::CreateFromInt32( nId ); + bThere = ( GetNamedObject( aGraphicName, 0, nDummy ) != NULL ); + } + + if ( pnCounter ) + *pnCounter = nId; + + return aGraphicName; +} + +void ScDrawLayer::EnsureGraphicNames() +{ + // make sure all graphic objects have names (after Excel import etc.) + + sal_uInt16 nTabCount = GetPageCount(); + for (sal_uInt16 nTab=0; nTab<nTabCount; nTab++) + { + SdrPage* pPage = GetPage(nTab); + DBG_ASSERT(pPage,"Page ?"); + if (pPage) + { + SdrObjListIter aIter( *pPage, IM_DEEPWITHGROUPS ); + SdrObject* pObject = aIter.Next(); + + /* #101799# The index passed to GetNewGraphicName() will be set to + the used index in each call. This prevents the repeated search + for all names from 1 to current index. */ + long nCounter = 0; + + while (pObject) + { + if ( pObject->GetObjIdentifier() == OBJ_GRAF && pObject->GetName().Len() == 0 ) + pObject->SetName( GetNewGraphicName( &nCounter ) ); + + pObject = aIter.Next(); + } + } + } +} + +void ScDrawLayer::SetAnchor( SdrObject* pObj, ScAnchorType eType ) +{ + ScAnchorType eOldAnchorType = GetAnchor( pObj ); + + // Ein an der Seite verankertes Objekt zeichnet sich durch eine Anker-Pos + // von (0,1) aus. Das ist ein shabby Trick, der aber funktioniert! + Point aAnchor( 0, eType == SCA_PAGE ? 1 : 0 ); + pObj->SetAnchorPos( aAnchor ); + + if ( eOldAnchorType != eType ) + pObj->notifyShapePropertyChange( ::svx::eSpreadsheetAnchor ); +} + +ScAnchorType ScDrawLayer::GetAnchor( const SdrObject* pObj ) +{ + Point aAnchor( pObj->GetAnchorPos() ); + return ( aAnchor.Y() != 0 ) ? SCA_PAGE : SCA_CELL; +} + +ScDrawObjData* ScDrawLayer::GetObjData( SdrObject* pObj, BOOL bCreate ) // static +{ + USHORT nCount = pObj ? pObj->GetUserDataCount() : 0; + for( USHORT i = 0; i < nCount; i++ ) + { + SdrObjUserData* pData = pObj->GetUserData( i ); + if( pData && pData->GetInventor() == SC_DRAWLAYER + && pData->GetId() == SC_UD_OBJDATA ) + return (ScDrawObjData*) pData; + } + if( pObj && bCreate ) + { + ScDrawObjData* pData = new ScDrawObjData; + pObj->InsertUserData( pData, 0 ); + return pData; + } + return 0; +} + +ScDrawObjData* ScDrawLayer::GetObjDataTab( SdrObject* pObj, SCTAB nTab ) // static +{ + ScDrawObjData* pData = GetObjData( pObj ); + if ( pData ) + { + if ( pData->maStart.IsValid() ) + pData->maStart.SetTab( nTab ); + if ( pData->maEnd.IsValid() ) + pData->maEnd.SetTab( nTab ); + } + return pData; +} + +bool ScDrawLayer::IsNoteCaption( SdrObject* pObj ) +{ + ScDrawObjData* pData = pObj ? GetObjData( pObj ) : 0; + return pData && pData->mbNote; +} + +ScDrawObjData* ScDrawLayer::GetNoteCaptionData( SdrObject* pObj, SCTAB nTab ) +{ + ScDrawObjData* pData = pObj ? GetObjDataTab( pObj, nTab ) : 0; + return (pData && pData->mbNote) ? pData : 0; +} + +ScIMapInfo* ScDrawLayer::GetIMapInfo( SdrObject* pObj ) // static +{ + USHORT nCount = pObj->GetUserDataCount(); + for( USHORT i = 0; i < nCount; i++ ) + { + SdrObjUserData* pData = pObj->GetUserData( i ); + if( pData && pData->GetInventor() == SC_DRAWLAYER + && pData->GetId() == SC_UD_IMAPDATA ) + return (ScIMapInfo*) pData; + } + return NULL; +} + +// static: +IMapObject* ScDrawLayer::GetHitIMapObject( SdrObject* pObj, + const Point& rWinPoint, const Window& rCmpWnd ) +{ + const MapMode aMap100( MAP_100TH_MM ); + MapMode aWndMode = rCmpWnd.GetMapMode(); + Point aRelPoint( rCmpWnd.LogicToLogic( rWinPoint, &aWndMode, &aMap100 ) ); + Rectangle aLogRect = rCmpWnd.LogicToLogic( pObj->GetLogicRect(), &aWndMode, &aMap100 ); + ScIMapInfo* pIMapInfo = GetIMapInfo( pObj ); + IMapObject* pIMapObj = NULL; + + if ( pIMapInfo ) + { + Size aGraphSize; + ImageMap& rImageMap = (ImageMap&) pIMapInfo->GetImageMap(); + Graphic aGraphic; + BOOL bObjSupported = FALSE; + + if ( pObj->ISA( SdrGrafObj ) ) // einfaches Grafik-Objekt + { + const SdrGrafObj* pGrafObj = (const SdrGrafObj*) pObj; + const GeoStat& rGeo = pGrafObj->GetGeoStat(); + const Graphic& rGraphic = pGrafObj->GetGraphic(); + + // Drehung rueckgaengig + if ( rGeo.nDrehWink ) + RotatePoint( aRelPoint, aLogRect.TopLeft(), -rGeo.nSin, rGeo.nCos ); + + // Spiegelung rueckgaengig + if ( ( (const SdrGrafObjGeoData*) pGrafObj->GetGeoData() )->bMirrored ) + aRelPoint.X() = aLogRect.Right() + aLogRect.Left() - aRelPoint.X(); + + // ggf. Unshear: + if ( rGeo.nShearWink ) + ShearPoint( aRelPoint, aLogRect.TopLeft(), -rGeo.nTan ); + + + if ( rGraphic.GetPrefMapMode().GetMapUnit() == MAP_PIXEL ) + aGraphSize = rCmpWnd.PixelToLogic( rGraphic.GetPrefSize(), + aMap100 ); + else + aGraphSize = OutputDevice::LogicToLogic( rGraphic.GetPrefSize(), + rGraphic.GetPrefMapMode(), + aMap100 ); + + bObjSupported = TRUE; + } + else if ( pObj->ISA( SdrOle2Obj ) ) // OLE-Objekt + { + // TODO/LEAN: working with visual area needs running state + aGraphSize = ((SdrOle2Obj*)pObj)->GetOrigObjSize(); + bObjSupported = TRUE; + } + + // hat alles geklappt, dann HitTest ausfuehren + if ( bObjSupported ) + { + // relativen Mauspunkt berechnen + aRelPoint -= aLogRect.TopLeft(); + pIMapObj = rImageMap.GetHitIMapObject( aGraphSize, aLogRect.GetSize(), aRelPoint ); + } + } + + return pIMapObj; +} + +ScMacroInfo* ScDrawLayer::GetMacroInfo( SdrObject* pObj, BOOL bCreate ) // static +{ + USHORT nCount = pObj->GetUserDataCount(); + for( USHORT i = 0; i < nCount; i++ ) + { + SdrObjUserData* pData = pObj->GetUserData( i ); + if( pData && pData->GetInventor() == SC_DRAWLAYER + && pData->GetId() == SC_UD_MACRODATA ) + return (ScMacroInfo*) pData; + } + if ( bCreate ) + { + ScMacroInfo* pData = new ScMacroInfo; + pObj->InsertUserData( pData, 0 ); + return pData; + } + return 0; +} + +void ScDrawLayer::SetGlobalDrawPersist(SfxObjectShell* pPersist) // static +{ + DBG_ASSERT(!pGlobalDrawPersist,"SetGlobalDrawPersist mehrfach"); + pGlobalDrawPersist = pPersist; +} + +void __EXPORT ScDrawLayer::SetChanged( sal_Bool bFlg /* = sal_True */ ) +{ + if ( bFlg && pDoc ) + pDoc->SetChartListenerCollectionNeedsUpdate( TRUE ); + FmFormModel::SetChanged( bFlg ); +} + +SvStream* __EXPORT ScDrawLayer::GetDocumentStream(SdrDocumentStreamInfo& rStreamInfo) const +{ + DBG_ASSERT( pDoc, "ScDrawLayer::GetDocumentStream without document" ); + if ( !pDoc ) + return NULL; + + uno::Reference< embed::XStorage > xStorage = pDoc->GetDocumentShell() ? + pDoc->GetDocumentShell()->GetStorage() : + NULL; + SvStream* pRet = NULL; + + if( xStorage.is() ) + { + if( rStreamInfo.maUserData.Len() && + ( rStreamInfo.maUserData.GetToken( 0, ':' ) == + String( RTL_CONSTASCII_USTRINGPARAM( "vnd.sun.star.Package" ) ) ) ) + { + const String aPicturePath( rStreamInfo.maUserData.GetToken( 1, ':' ) ); + + // graphic from picture stream in picture storage in XML package + if( aPicturePath.GetTokenCount( '/' ) == 2 ) + { + const String aPictureStreamName( aPicturePath.GetToken( 1, '/' ) ); + const String aPictureStorageName( aPicturePath.GetToken( 0, '/' ) ); + + try { + if ( xStorage->isStorageElement( aPictureStorageName ) ) + { + uno::Reference< embed::XStorage > xPictureStorage = + xStorage->openStorageElement( aPictureStorageName, embed::ElementModes::READ ); + + if( xPictureStorage.is() && + xPictureStorage->isStreamElement( aPictureStreamName ) ) + { + uno::Reference< io::XStream > xStream = + xPictureStorage->openStreamElement( aPictureStreamName, embed::ElementModes::READ ); + if ( xStream.is() ) + pRet = ::utl::UcbStreamHelper::CreateStream( xStream ); + } + } + } + catch( uno::Exception& ) + { + // TODO: error handling + } + } + } + // the following code seems to be related to binary format +//REMOVE else +//REMOVE { +//REMOVE pRet = pStor->OpenStream( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(STRING_SCSTREAM)), +//REMOVE STREAM_READ | STREAM_WRITE | STREAM_TRUNC ); +//REMOVE +//REMOVE if( pRet ) +//REMOVE { +//REMOVE pRet->SetVersion( pStor->GetVersion() ); +//REMOVE pRet->SetKey( pStor->GetKey() ); +//REMOVE } +//REMOVE } + + rStreamInfo.mbDeleteAfterUse = ( pRet != NULL ); + } + + return pRet; +} + +//REMOVE void ScDrawLayer::ReleasePictureStorage() +//REMOVE { +//REMOVE xPictureStorage.Clear(); +//REMOVE } + +SdrLayerID __EXPORT ScDrawLayer::GetControlExportLayerId( const SdrObject & ) const +{ + // Layer fuer Export von Form-Controls in Versionen vor 5.0 - immer SC_LAYER_FRONT + return SC_LAYER_FRONT; +} + +::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > ScDrawLayer::createUnoModel() +{ + ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > xRet; + if( pDoc && pDoc->GetDocumentShell() ) + xRet = pDoc->GetDocumentShell()->GetModel(); + + return xRet; +} diff --git a/sc/source/core/data/fillinfo.cxx b/sc/source/core/data/fillinfo.cxx new file mode 100644 index 000000000000..c8eba91d2cc1 --- /dev/null +++ b/sc/source/core/data/fillinfo.cxx @@ -0,0 +1,1064 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: fillinfo.cxx,v $ + * $Revision: 1.15 $ + * + * 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 "scitems.hxx" +#include <svx/boxitem.hxx> +#include <svx/bolnitem.hxx> +#include <svx/editdata.hxx> // can be removed if table has a bLayoutRTL flag +#include <svx/shaditem.hxx> + +#include "fillinfo.hxx" +#include "document.hxx" +#include "cell.hxx" +#include "table.hxx" +#include "attrib.hxx" +#include "attarray.hxx" +#include "markarr.hxx" +#include "markdata.hxx" +#include "patattr.hxx" +#include "poolhelp.hxx" +#include "docpool.hxx" +#include "conditio.hxx" +#include "stlpool.hxx" + + +// ----------------------------------------------------------------------- + +const USHORT ROWINFO_MAX = 1024; + + +enum FillInfoLinePos + { + FILP_TOP, + FILP_BOTTOM, + FILP_LEFT, + FILP_RIGHT + }; + + +inline const SvxBorderLine* GetNullOrLine( const SvxBoxItem* pBox, FillInfoLinePos eWhich ) +{ + if (pBox) + { + if (eWhich==FILP_TOP) + return pBox->GetTop(); + else if (eWhich==FILP_BOTTOM) + return pBox->GetBottom(); + else if (eWhich==FILP_LEFT) + return pBox->GetLeft(); + else + return pBox->GetRight(); + } + else + return NULL; +} + +// aehnlich wie in output.cxx + +void lcl_GetMergeRange( SCsCOL nX, SCsROW nY, SCSIZE nArrY, + ScDocument* pDoc, RowInfo* pRowInfo, + SCCOL nX1, SCROW nY1, SCCOL /* nX2 */, SCROW /* nY2 */, SCTAB nTab, + SCsCOL& rStartX, SCsROW& rStartY, SCsCOL& rEndX, SCsROW& rEndY ) +{ + CellInfo* pInfo = &pRowInfo[nArrY].pCellInfo[nX+1]; + + rStartX = nX; + rStartY = nY; + BOOL bHOver = pInfo->bHOverlapped; + BOOL bVOver = pInfo->bVOverlapped; + + while (bHOver) // nY konstant + { + --rStartX; + if (rStartX >= (SCsCOL) nX1 && (pDoc->GetColFlags(rStartX,nTab) & CR_HIDDEN) == 0) + { + bHOver = pRowInfo[nArrY].pCellInfo[rStartX+1].bHOverlapped; + bVOver = pRowInfo[nArrY].pCellInfo[rStartX+1].bVOverlapped; + } + else + { + USHORT nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr( + rStartX, rStartY, nTab, ATTR_MERGE_FLAG ))->GetValue(); + bHOver = ((nOverlap & SC_MF_HOR) != 0); + bVOver = ((nOverlap & SC_MF_VER) != 0); + } + } + + while (bVOver) + { + --rStartY; + + if (nArrY>0) + --nArrY; // lokale Kopie ! + + if (rStartX >= (SCsCOL) nX1 && rStartY >= (SCsROW) nY1 && + (pDoc->GetColFlags(rStartX,nTab) & CR_HIDDEN) == 0 && + (pDoc->GetRowFlags(rStartY,nTab) & CR_HIDDEN) == 0 && + (SCsROW) pRowInfo[nArrY].nRowNo == rStartY) + { + bHOver = pRowInfo[nArrY].pCellInfo[rStartX+1].bHOverlapped; + bVOver = pRowInfo[nArrY].pCellInfo[rStartX+1].bVOverlapped; + } + else + { + USHORT nOverlap = ((ScMergeFlagAttr*)pDoc->GetAttr( + rStartX, rStartY, nTab, ATTR_MERGE_FLAG ))->GetValue(); + bHOver = ((nOverlap & SC_MF_HOR) != 0); + bVOver = ((nOverlap & SC_MF_VER) != 0); + } + } + + const ScMergeAttr* pMerge; + if (rStartX >= (SCsCOL) nX1 && rStartY >= (SCsROW) nY1 && + (pDoc->GetColFlags(rStartX,nTab) & CR_HIDDEN) == 0 && + (pDoc->GetRowFlags(rStartY,nTab) & CR_HIDDEN) == 0 && + (SCsROW) pRowInfo[nArrY].nRowNo == rStartY) + { + pMerge = (const ScMergeAttr*) &pRowInfo[nArrY].pCellInfo[rStartX+1].pPatternAttr-> + GetItem(ATTR_MERGE); + } + else + pMerge = (const ScMergeAttr*) pDoc->GetAttr(rStartX,rStartY,nTab,ATTR_MERGE); + + rEndX = rStartX + pMerge->GetColMerge() - 1; + rEndY = rStartY + pMerge->GetRowMerge() - 1; +} + +inline BOOL ScDocument::RowHidden( SCROW nRow, SCTAB nTab ) +{ + return ( pTab[nTab]->pRowFlags->GetValue(nRow) & CR_HIDDEN ) != 0; +} + + +#define CELLINFO(x,y) pRowInfo[nArrY+y].pCellInfo[nArrX+x] + +void ScDocument::FillInfo( ScTableInfo& rTabInfo, SCCOL nX1, SCROW nY1, SCCOL nX2, SCROW nY2, + SCTAB nTab, double nScaleX, double nScaleY, + BOOL bPageMode, BOOL bFormulaMode, const ScMarkData* pMarkData ) +{ + DBG_ASSERT( pTab[nTab], "Tabelle existiert nicht" ); + + BOOL bLayoutRTL = IsLayoutRTL( nTab ); + + ScDocumentPool* pPool = xPoolHelper->GetDocPool(); + ScStyleSheetPool* pStlPool = xPoolHelper->GetStylePool(); + + RowInfo* pRowInfo = rTabInfo.mpRowInfo; + + const SvxBrushItem* pDefBackground = + (const SvxBrushItem*) &pPool->GetDefaultItem( ATTR_BACKGROUND ); + const ScMergeAttr* pDefMerge = + (const ScMergeAttr*) &pPool->GetDefaultItem( ATTR_MERGE ); + const SvxShadowItem* pDefShadow = + (const SvxShadowItem*) &pPool->GetDefaultItem( ATTR_SHADOW ); + + SCROW nThisRow; + SCCOL nX; + SCROW nY; + SCsROW nSignedY; + SCCOL nArrX; + SCSIZE nArrY; + SCSIZE nArrCount; + BOOL bAnyMerged = FALSE; + BOOL bAnyShadow = FALSE; + BOOL bAnyCondition = FALSE; + + BOOL bTabProtect = IsTabProtected(nTab); + + // fuer Blockmarken von zusammengefassten Zellen mit + // versteckter erster Zeile / Spalte + BOOL bPaintMarks = FALSE; + BOOL bSkipMarks = FALSE; + SCCOL nBlockStartX = 0, nBlockEndX = 0; + SCROW nBlockEndY = 0, nBlockStartY = 0; + if (pMarkData && pMarkData->IsMarked()) + { + ScRange aTmpRange; + pMarkData->GetMarkArea(aTmpRange); + if ( nTab >= aTmpRange.aStart.Tab() && nTab <= aTmpRange.aEnd.Tab() ) + { + nBlockStartX = aTmpRange.aStart.Col(); + nBlockStartY = aTmpRange.aStart.Row(); + nBlockEndX = aTmpRange.aEnd.Col(); + nBlockEndY = aTmpRange.aEnd.Row(); + ExtendHidden( nBlockStartX, nBlockStartY, nBlockEndX, nBlockEndY, nTab ); //? noetig ? + if (pMarkData->IsMarkNegative()) + bSkipMarks = TRUE; + else + bPaintMarks = TRUE; + } + } + + // zuerst nur die Eintraege fuer die ganze Spalte + + nArrY=0; + SCROW nYExtra = nY2+1; + for (nSignedY=((SCsROW)nY1)-1; nSignedY<=(SCsROW)nYExtra; nSignedY++) + { + if (nSignedY >= 0) + nY = (SCROW) nSignedY; + else + nY = MAXROW+1; // ungueltig + + USHORT nDocHeight; + if (ValidRow(nY)) + nDocHeight = GetRowHeight( nY, nTab ); + else + nDocHeight = ScGlobal::nStdRowHeight; + + if ( nArrY==0 || nDocHeight || nY > MAXROW ) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + pThisRowInfo->pCellInfo = NULL; // wird unten belegt + + USHORT nHeight = (USHORT) ( nDocHeight * nScaleY ); + if (!nHeight) + nHeight = 1; + + pThisRowInfo->nRowNo = nY; //! Fall < 0 ? + pThisRowInfo->nHeight = nHeight; + pThisRowInfo->bEmptyBack = TRUE; + pThisRowInfo->bEmptyText = TRUE; + pThisRowInfo->bChanged = TRUE; + pThisRowInfo->bAutoFilter = FALSE; + pThisRowInfo->bPushButton = FALSE; + pThisRowInfo->nRotMaxCol = SC_ROTMAX_NONE; + + ++nArrY; + if (nArrY >= ROWINFO_MAX) + { + DBG_ERROR("Zu grosser Bereich bei FillInfo" ); + nYExtra = nSignedY; // Ende + nY2 = nYExtra - 1; // Bereich anpassen + } + } + else + if (nSignedY==(SCsROW) nYExtra) // zusaetzliche Zeile verdeckt ? + ++nYExtra; + } + nArrCount = nArrY; // incl. Dummys + + // rotierter Text... + + // Attribut im Dokument ueberhaupt verwendet? + BOOL bAnyItem = FALSE; + USHORT nRotCount = pPool->GetItemCount( ATTR_ROTATE_VALUE ); + for (USHORT nItem=0; nItem<nRotCount; nItem++) + if (pPool->GetItem( ATTR_ROTATE_VALUE, nItem )) + { + bAnyItem = TRUE; + break; + } + + SCCOL nRotMax = nX2; + if ( bAnyItem && HasAttrib( 0,nY1,nTab, MAXCOL,nY2+1,nTab, + HASATTR_ROTATE | HASATTR_CONDITIONAL ) ) + { + //! Conditionals auch bei HASATTR_ROTATE abfragen ???? + + DBG_ASSERT( nArrCount>2, "nArrCount zu klein" ); +// FindMaxRotCol( nTab, &pRowInfo[1], nArrCount-2, nX1, nX2 ); + FindMaxRotCol( nTab, &pRowInfo[1], nArrCount-1, nX1, nX2 ); + // FindMaxRotCol setzt nRotMaxCol + + for (nArrY=0; nArrY<nArrCount; nArrY++) + if (pRowInfo[nArrY].nRotMaxCol != SC_ROTMAX_NONE && pRowInfo[nArrY].nRotMaxCol > nRotMax) + nRotMax = pRowInfo[nArrY].nRotMaxCol; + } + + // Zell-Infos erst nach dem Test auf gedrehte allozieren + // bis nRotMax wegen nRotateDir Flag + + for (nArrY=0; nArrY<nArrCount; nArrY++) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + nY = pThisRowInfo->nRowNo; + pThisRowInfo->pCellInfo = new CellInfo[ nRotMax+1+2 ]; // vom Aufrufer zu loeschen ! + + for (nArrX=0; nArrX<=nRotMax+2; nArrX++) // Zell-Infos vorbelegen + { + if (nArrX>0) + nX = nArrX-1; + else + nX = MAXCOL+1; // ungueltig + + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + pInfo->bEmptyCellText = TRUE; + pInfo->pCell = NULL; + if (bPaintMarks) + pInfo->bMarked = ( nX >= nBlockStartX && nX <= nBlockEndX + && nY >= nBlockStartY && nY <= nBlockEndY ); + else + pInfo->bMarked = FALSE; + pInfo->nWidth = 0; + + pInfo->nClipMark = SC_CLIPMARK_NONE; + pInfo->bMerged = FALSE; + pInfo->bHOverlapped = FALSE; + pInfo->bVOverlapped = FALSE; + pInfo->bAutoFilter = FALSE; + pInfo->bPushButton = FALSE; + pInfo->nRotateDir = SC_ROTDIR_NONE; + + pInfo->bPrinted = FALSE; // view-intern + pInfo->bHideGrid = FALSE; // view-intern + pInfo->bEditEngine = FALSE; // view-intern + + pInfo->pBackground = NULL; //! weglassen? + pInfo->pPatternAttr = NULL; + pInfo->pConditionSet= NULL; + + pInfo->pLinesAttr = NULL; + pInfo->mpTLBRLine = NULL; + pInfo->mpBLTRLine = NULL; + + pInfo->pShadowAttr = pDefShadow; + pInfo->pHShadowOrigin = NULL; + pInfo->pVShadowOrigin = NULL; + } + } + + for (nArrX=nX2+3; nArrX<=nRotMax+2; nArrX++) // restliche Breiten eintragen + { + nX = nArrX-1; + if ( ValidCol(nX) ) + { + if ( (GetColFlags(nX,nTab) & CR_HIDDEN) == 0 ) // Spalte nicht versteckt + { + USHORT nThisWidth = (USHORT) (GetColWidth( nX, nTab ) * nScaleX); + if (!nThisWidth) + nThisWidth = 1; + + pRowInfo[0].pCellInfo[nArrX].nWidth = nThisWidth; + } + } + } + + for (nArrX=0; nArrX<=nX2+2; nArrX++) // links & rechts + 1 + { + nX = (nArrX>0) ? nArrX-1 : MAXCOL+1; // negativ -> ungueltig + + if ( ValidCol(nX) ) + { + // #i58049#, #i57939# Hidden columns must be skipped here, or their attributes + // will disturb the output + + if ( (GetColFlags(nX,nTab) & CR_HIDDEN) == 0 ) // column not hidden + { + USHORT nThisWidth = (USHORT) (GetColWidth( nX, nTab ) * nScaleX); + if (!nThisWidth) + nThisWidth = 1; + + pRowInfo[0].pCellInfo[nArrX].nWidth = nThisWidth; //! dies sollte reichen + + ScColumn* pThisCol = &pTab[nTab]->aCol[nX]; // Spalten-Daten + + nArrY = 1; + SCSIZE nUIndex; + (void) pThisCol->Search( nY1, nUIndex ); + while ( nUIndex < pThisCol->nCount && + (nThisRow=pThisCol->pItems[nUIndex].nRow) <= nY2 ) + { + if ( !RowHidden( nThisRow,nTab ) ) + { + while ( pRowInfo[nArrY].nRowNo < nThisRow ) + ++nArrY; + DBG_ASSERT( pRowInfo[nArrY].nRowNo == nThisRow, "Zeile nicht gefunden in FillInfo" ); + + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + pInfo->pCell = pThisCol->pItems[nUIndex].pCell; + if (pInfo->pCell->GetCellType() != CELLTYPE_NOTE) + { + pThisRowInfo->bEmptyText = FALSE; // Zeile nicht leer + pInfo->bEmptyCellText = FALSE; // Zelle nicht leer + } + ++nArrY; + } + ++nUIndex; + } + + if (nX+1 >= nX1) // Attribute/Blockmarken ab nX1-1 + { + ScAttrArray* pThisAttrArr = pThisCol->pAttrArray; // Attribute + + nArrY = 0; + const ScPatternAttr* pPattern; + SCROW nCurRow=nY1; // einzelne Zeile + if (nCurRow>0) + --nCurRow; // oben 1 mehr + else + nArrY = 1; + nThisRow=nCurRow; // Ende des Bereichs + SCSIZE nIndex; + (void) pThisAttrArr->Search( nCurRow, nIndex ); + + + do + { + nThisRow=pThisAttrArr->pData[nIndex].nRow; // Ende des Bereichs + pPattern=pThisAttrArr->pData[nIndex].pPattern; + + const SvxBrushItem* pBackground = (const SvxBrushItem*) + &pPattern->GetItem(ATTR_BACKGROUND); + const SvxBoxItem* pLinesAttr = (const SvxBoxItem*) + &pPattern->GetItem(ATTR_BORDER); + + const SvxLineItem* pTLBRLine = static_cast< const SvxLineItem* >( + &pPattern->GetItem( ATTR_BORDER_TLBR ) ); + const SvxLineItem* pBLTRLine = static_cast< const SvxLineItem* >( + &pPattern->GetItem( ATTR_BORDER_BLTR ) ); + + const SvxShadowItem* pShadowAttr = (const SvxShadowItem*) + &pPattern->GetItem(ATTR_SHADOW); + if (pShadowAttr != pDefShadow) + bAnyShadow = TRUE; + + const ScMergeAttr* pMergeAttr = (const ScMergeAttr*) + &pPattern->GetItem(ATTR_MERGE); + BOOL bMerged = ( pMergeAttr != pDefMerge && *pMergeAttr != *pDefMerge ); + USHORT nOverlap = ((const ScMergeFlagAttr*) &pPattern->GetItemSet(). + Get(ATTR_MERGE_FLAG))->GetValue(); + BOOL bHOverlapped = ((nOverlap & SC_MF_HOR) != 0); + BOOL bVOverlapped = ((nOverlap & SC_MF_VER) != 0); + BOOL bAutoFilter = ((nOverlap & SC_MF_AUTO) != 0); + BOOL bPushButton = ((nOverlap & SC_MF_BUTTON) != 0); + BOOL bScenario = ((nOverlap & SC_MF_SCENARIO) != 0); + if (bMerged||bHOverlapped||bVOverlapped) + bAnyMerged = TRUE; // intern + + BOOL bHidden, bHideFormula; + if (bTabProtect) + { + const ScProtectionAttr& rProtAttr = (const ScProtectionAttr&) + pPattern->GetItem(ATTR_PROTECTION); + bHidden = rProtAttr.GetHideCell(); + bHideFormula = rProtAttr.GetHideFormula(); + } + else + bHidden = bHideFormula = FALSE; + + ULONG nConditional = ((const SfxUInt32Item&)pPattern-> + GetItem(ATTR_CONDITIONAL)).GetValue(); + const ScConditionalFormat* pCondForm = NULL; + if ( nConditional && pCondFormList ) + pCondForm = pCondFormList->GetFormat( nConditional ); + + do + { + if ( nArrY==0 || !RowHidden( nCurRow,nTab ) ) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + if (pBackground != pDefBackground) // Spalten-HG == Standard ? + pThisRowInfo->bEmptyBack = FALSE; + if (bAutoFilter) + pThisRowInfo->bAutoFilter = TRUE; + if (bPushButton) + pThisRowInfo->bPushButton = TRUE; + + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + pInfo->pBackground = pBackground; + pInfo->pPatternAttr = pPattern; + pInfo->bMerged = bMerged; + pInfo->bHOverlapped = bHOverlapped; + pInfo->bVOverlapped = bVOverlapped; + pInfo->bAutoFilter = bAutoFilter; + pInfo->bPushButton = bPushButton; + pInfo->pLinesAttr = pLinesAttr; + pInfo->mpTLBRLine = pTLBRLine; + pInfo->mpBLTRLine = pBLTRLine; + pInfo->pShadowAttr = pShadowAttr; + // nWidth wird nicht mehr einzeln gesetzt + + BOOL bEmbed = FALSE; //bIsEmbedded && + nTab >= aEmbedRange.aStart.Tab() && + nTab <= aEmbedRange.aEnd.Tab() && + nX >= aEmbedRange.aStart.Col() && + nX <= aEmbedRange.aEnd.Col() && + nCurRow >= aEmbedRange.aStart.Row() && + nCurRow <= aEmbedRange.aEnd.Row(); + + if (bPushButton || bScenario) + { + pInfo->pBackground = ScGlobal::GetButtonBrushItem(); + pThisRowInfo->bEmptyBack = FALSE; + } + else if (bEmbed) + { + pInfo->pBackground = ScGlobal::GetEmbeddedBrushItem(); + pThisRowInfo->bEmptyBack = FALSE; + } + + if (bHidden || ( bFormulaMode && bHideFormula && pInfo->pCell + && pInfo->pCell->GetCellType() + == CELLTYPE_FORMULA )) + pInfo->bEmptyCellText = TRUE; + + if ( pCondForm ) + { + String aStyle = pCondForm->GetCellStyle( pInfo->pCell, + ScAddress( nX, nCurRow, nTab ) ); + if (aStyle.Len()) + { + SfxStyleSheetBase* pStyleSheet = + pStlPool->Find( aStyle, SFX_STYLE_FAMILY_PARA ); + if ( pStyleSheet ) + { + //! Style-Sets cachen !!! + pInfo->pConditionSet = &pStyleSheet->GetItemSet(); + bAnyCondition = TRUE; + } + // if style is not there, treat like no condition + } + } + + ++nArrY; + } + ++nCurRow; + } + while (nCurRow <= nThisRow && nCurRow <= nYExtra); + ++nIndex; + } + while ( nIndex < pThisAttrArr->nCount && nThisRow < nYExtra ); + + + if (pMarkData && pMarkData->IsMultiMarked()) + { + // Blockmarken + const ScMarkArray* pThisMarkArr = pMarkData->GetArray()+nX; + BOOL bThisMarked; + nArrY = 1; + nCurRow = nY1; // einzelne Zeile + nThisRow = nY1; // Ende des Bereichs + + if ( pThisMarkArr->Search( nY1, nIndex ) ) + { + do + { + nThisRow=pThisMarkArr->pData[nIndex].nRow; // Ende des Bereichs + bThisMarked=pThisMarkArr->pData[nIndex].bMarked; + + do + { + if ( !RowHidden( nCurRow,nTab ) ) + { + if ( bThisMarked ) + { + BOOL bSkip = bSkipMarks && + nX >= nBlockStartX && + nX <= nBlockEndX && + nCurRow >= nBlockStartY && + nCurRow <= nBlockEndY; + if (!bSkip) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + pInfo->bMarked = TRUE; + } + } + ++nArrY; + } + ++nCurRow; + } + while (nCurRow <= nThisRow && nCurRow <= nY2); + ++nIndex; + } + while ( nIndex < pThisMarkArr->nCount && nThisRow < nY2 ); + } + } + } + else // vordere Spalten + { + for (nArrY=1; nArrY+1<nArrCount; nArrY++) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + + pInfo->nWidth = nThisWidth; //! oder nur 0 abfragen ?? + } + } + } + } + else + pRowInfo[0].pCellInfo[nArrX].nWidth = STD_COL_WIDTH; + // STD_COL_WIDTH ganz links und rechts wird fuer DrawExtraShadow gebraucht + } + + //------------------------------------------------------------------------- + // bedingte Formatierung auswerten + + if (bAnyCondition) + { + for (nArrY=0; nArrY<nArrCount; nArrY++) + { + for (nArrX=nX1; nArrX<=nX2+2; nArrX++) // links und rechts einer mehr + { + CellInfo* pInfo = &pRowInfo[nArrY].pCellInfo[nArrX]; + const SfxItemSet* pCondSet = pInfo->pConditionSet; + if (pCondSet) + { + const SfxPoolItem* pItem; + + // Hintergrund + if ( pCondSet->GetItemState( ATTR_BACKGROUND, TRUE, &pItem ) == SFX_ITEM_SET ) + { + pInfo->pBackground = (const SvxBrushItem*) pItem; + pRowInfo[nArrY].bEmptyBack = FALSE; + } + + // Umrandung + if ( pCondSet->GetItemState( ATTR_BORDER, TRUE, &pItem ) == SFX_ITEM_SET ) + pInfo->pLinesAttr = (const SvxBoxItem*) pItem; + + if ( pCondSet->GetItemState( ATTR_BORDER_TLBR, TRUE, &pItem ) == SFX_ITEM_SET ) + pInfo->mpTLBRLine = static_cast< const SvxLineItem* >( pItem ); + if ( pCondSet->GetItemState( ATTR_BORDER_BLTR, TRUE, &pItem ) == SFX_ITEM_SET ) + pInfo->mpBLTRLine = static_cast< const SvxLineItem* >( pItem ); + + // Schatten + if ( pCondSet->GetItemState( ATTR_SHADOW, TRUE, &pItem ) == SFX_ITEM_SET ) + { + pInfo->pShadowAttr = (const SvxShadowItem*) pItem; + bAnyShadow = TRUE; + } + } + } + } + } + + // bedingte Formatierung Ende + //------------------------------------------------------------------------- + + // + // Daten von zusammengefassten Zellen anpassen + // + + if (bAnyMerged) + { + for (nArrY=0; nArrY<nArrCount; nArrY++) + { + RowInfo* pThisRowInfo = &pRowInfo[nArrY]; + nSignedY = nArrY ? pThisRowInfo->nRowNo : ((SCsROW)nY1)-1; + + for (nArrX=nX1; nArrX<=nX2+2; nArrX++) // links und rechts einer mehr + { + SCsCOL nSignedX = ((SCsCOL) nArrX) - 1; + CellInfo* pInfo = &pThisRowInfo->pCellInfo[nArrX]; + + if (pInfo->bMerged || pInfo->bHOverlapped || pInfo->bVOverlapped) + { + SCsCOL nStartX; + SCsROW nStartY; + SCsCOL nEndX; + SCsROW nEndY; + lcl_GetMergeRange( nSignedX,nSignedY, nArrY, this,pRowInfo, nX1,nY1,nX2,nY2,nTab, + nStartX,nStartY, nEndX,nEndY ); + const ScPatternAttr* pStartPattern = GetPattern( nStartX,nStartY,nTab ); + const SfxItemSet* pStartCond = GetCondResult( nStartX,nStartY,nTab ); + const SfxPoolItem* pItem; + + // Hintergrund kopieren (oder in output.cxx) + + if ( !pStartCond || pStartCond-> + GetItemState(ATTR_BACKGROUND,TRUE,&pItem) != SFX_ITEM_SET ) + pItem = &pStartPattern->GetItem(ATTR_BACKGROUND); + pInfo->pBackground = (const SvxBrushItem*) pItem; + pRowInfo[nArrY].bEmptyBack = FALSE; + + // Schatten + + if ( !pStartCond || pStartCond-> + GetItemState(ATTR_SHADOW,TRUE,&pItem) != SFX_ITEM_SET ) + pItem = &pStartPattern->GetItem(ATTR_SHADOW); + pInfo->pShadowAttr = (const SvxShadowItem*) pItem; + if (pInfo->pShadowAttr != pDefShadow) + bAnyShadow = TRUE; + + // Blockmarken - wieder mit Original-Merge-Werten + + BOOL bCellMarked = FALSE; + if (bPaintMarks) + bCellMarked = ( nStartX >= (SCsCOL) nBlockStartX + && nStartX <= (SCsCOL) nBlockEndX + && nStartY >= (SCsROW) nBlockStartY + && nStartY <= (SCsROW) nBlockEndY ); + if (pMarkData && pMarkData->IsMultiMarked() && !bCellMarked) + { + const ScMarkArray* pThisMarkArr = pMarkData->GetArray()+nStartX; + SCSIZE nIndex; + if ( pThisMarkArr->Search( nStartY, nIndex ) ) + bCellMarked=pThisMarkArr->pData[nIndex].bMarked; + } + + pInfo->bMarked = bCellMarked; + } + } + } + } + + if (bAnyShadow) // Schatten verteilen + { + for (nArrY=0; nArrY<nArrCount; nArrY++) + { + BOOL bTop = ( nArrY == 0 ); + BOOL bBottom = ( nArrY+1 == nArrCount ); + + for (nArrX=nX1; nArrX<=nX2+2; nArrX++) // links und rechts einer mehr + { + BOOL bLeft = ( nArrX == nX1 ); + BOOL bRight = ( nArrX == nX2+2 ); + + CellInfo* pInfo = &pRowInfo[nArrY].pCellInfo[nArrX]; + const SvxShadowItem* pThisAttr = pInfo->pShadowAttr; + SvxShadowLocation eLoc = pThisAttr ? pThisAttr->GetLocation() : SVX_SHADOW_NONE; + if (eLoc != SVX_SHADOW_NONE) + { + // oder Test auf != eLoc + + SCsCOL nDxPos = 1; + SCsCOL nDxNeg = -1; + + while ( nArrX+nDxPos < nX2+2 && pRowInfo[0].pCellInfo[nArrX+nDxPos].nWidth == 0 ) + ++nDxPos; + while ( nArrX+nDxNeg > nX1 && pRowInfo[0].pCellInfo[nArrX+nDxNeg].nWidth == 0 ) + --nDxNeg; + + BOOL bLeftDiff = !bLeft && + CELLINFO(nDxNeg,0).pShadowAttr->GetLocation() == SVX_SHADOW_NONE; + BOOL bRightDiff = !bRight && + CELLINFO(nDxPos,0).pShadowAttr->GetLocation() == SVX_SHADOW_NONE; + BOOL bTopDiff = !bTop && + CELLINFO(0,-1).pShadowAttr->GetLocation() == SVX_SHADOW_NONE; + BOOL bBottomDiff = !bBottom && + CELLINFO(0,1).pShadowAttr->GetLocation() == SVX_SHADOW_NONE; + + if ( bLayoutRTL ) + { + switch (eLoc) + { + case SVX_SHADOW_BOTTOMRIGHT: eLoc = SVX_SHADOW_BOTTOMLEFT; break; + case SVX_SHADOW_BOTTOMLEFT: eLoc = SVX_SHADOW_BOTTOMRIGHT; break; + case SVX_SHADOW_TOPRIGHT: eLoc = SVX_SHADOW_TOPLEFT; break; + case SVX_SHADOW_TOPLEFT: eLoc = SVX_SHADOW_TOPRIGHT; break; + default: + { + // added to avoid warnings + } + } + } + + switch (eLoc) + { + case SVX_SHADOW_BOTTOMRIGHT: + if (bBottomDiff) + { + CELLINFO(0,1).pHShadowOrigin = pThisAttr; + CELLINFO(0,1).eHShadowPart = + bLeftDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ; + } + if (bRightDiff) + { + CELLINFO(1,0).pVShadowOrigin = pThisAttr; + CELLINFO(1,0).eVShadowPart = + bTopDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT; + } + if (bBottomDiff && bRightDiff) + { + CELLINFO(1,1).pHShadowOrigin = pThisAttr; + CELLINFO(1,1).eHShadowPart = SC_SHADOW_CORNER; + } + break; + + case SVX_SHADOW_BOTTOMLEFT: + if (bBottomDiff) + { + CELLINFO(0,1).pHShadowOrigin = pThisAttr; + CELLINFO(0,1).eHShadowPart = + bRightDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ; + } + if (bLeftDiff) + { + CELLINFO(-1,0).pVShadowOrigin = pThisAttr; + CELLINFO(-1,0).eVShadowPart = + bTopDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT; + } + if (bBottomDiff && bLeftDiff) + { + CELLINFO(-1,1).pHShadowOrigin = pThisAttr; + CELLINFO(-1,1).eHShadowPart = SC_SHADOW_CORNER; + } + break; + + case SVX_SHADOW_TOPRIGHT: + if (bTopDiff) + { + CELLINFO(0,-1).pHShadowOrigin = pThisAttr; + CELLINFO(0,-1).eHShadowPart = + bLeftDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ; + } + if (bRightDiff) + { + CELLINFO(1,0).pVShadowOrigin = pThisAttr; + CELLINFO(1,0).eVShadowPart = + bBottomDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT; + } + if (bTopDiff && bRightDiff) + { + CELLINFO(1,-1).pHShadowOrigin = pThisAttr; + CELLINFO(1,-1).eHShadowPart = SC_SHADOW_CORNER; + } + break; + + case SVX_SHADOW_TOPLEFT: + if (bTopDiff) + { + CELLINFO(0,-1).pHShadowOrigin = pThisAttr; + CELLINFO(0,-1).eHShadowPart = + bRightDiff ? SC_SHADOW_HSTART : SC_SHADOW_HORIZ; + } + if (bLeftDiff) + { + CELLINFO(-1,0).pVShadowOrigin = pThisAttr; + CELLINFO(-1,0).eVShadowPart = + bBottomDiff ? SC_SHADOW_VSTART : SC_SHADOW_VERT; + } + if (bTopDiff && bLeftDiff) + { + CELLINFO(-1,-1).pHShadowOrigin = pThisAttr; + CELLINFO(-1,-1).eHShadowPart = SC_SHADOW_CORNER; + } + break; + + default: + DBG_ERROR("falscher Shadow-Enum"); + } + } + } + } + } + + rTabInfo.mnArrCount = sal::static_int_cast<USHORT>(nArrCount); + rTabInfo.mbPageMode = bPageMode; + + // ======================================================================== + // *** create the frame border array *** + + // RowInfo structs are filled in the range [ 0 , nArrCount-1 ] + // each RowInfo contains CellInfo structs in the range [ nX1-1 , nX2+1 ] + + size_t nColCount = nX2 - nX1 + 3; + size_t nRowCount = nArrCount; + + svx::frame::Array& rArray = rTabInfo.maArray; + rArray.Initialize( nColCount, nRowCount ); + rArray.SetUseDiagDoubleClipping( false ); + + for( size_t nRow = 0; nRow < nRowCount; ++nRow ) + { + USHORT nCellInfoY = static_cast< USHORT >( nRow ); + RowInfo& rThisRowInfo = pRowInfo[ nCellInfoY ]; + + for( size_t nCol = 0; nCol < nColCount; ++nCol ) + { + USHORT nCellInfoX = static_cast< USHORT >( nCol + nX1 ); + const CellInfo& rInfo = rThisRowInfo.pCellInfo[ nCellInfoX ]; + + const SvxBoxItem* pBox = rInfo.pLinesAttr; + const SvxLineItem* pTLBR = rInfo.mpTLBRLine; + const SvxLineItem* pBLTR = rInfo.mpBLTRLine; + + size_t nFirstCol = nCol; + size_t nFirstRow = nRow; + + // *** merged cells *** ------------------------------------------- + + if( !rArray.IsMerged( nCol, nRow ) && (rInfo.bMerged || rInfo.bHOverlapped || rInfo.bVOverlapped) ) + { + // *** insert merged range in svx::frame::Array *** + + /* #i69369# top-left cell of a merged range may be located in + a hidden column or row. Use lcl_GetMergeRange() to find the + complete merged range, then calculate dimensions and + document position of the visible range. */ + + // note: document columns are always one less than CellInfoX coords + // note: document rows must be looked up in RowInfo structs + + // current column and row in document coordinates + SCCOL nCurrDocCol = static_cast< SCCOL >( nCellInfoX - 1 ); + SCROW nCurrDocRow = static_cast< SCROW >( (nCellInfoY > 0) ? rThisRowInfo.nRowNo : (nY1 - 1) ); + + // find entire merged range in document, returns signed document coordinates + SCsCOL nFirstRealDocColS, nLastRealDocColS; + SCsROW nFirstRealDocRowS, nLastRealDocRowS; + lcl_GetMergeRange( static_cast< SCsCOL >( nCurrDocCol ), static_cast< SCsROW >( nCurrDocRow ), + nCellInfoY, this, pRowInfo, nX1,nY1,nX2,nY2,nTab, + nFirstRealDocColS, nFirstRealDocRowS, nLastRealDocColS, nLastRealDocRowS ); + + // *complete* merged range in document coordinates + SCCOL nFirstRealDocCol = static_cast< SCCOL >( nFirstRealDocColS ); + SCROW nFirstRealDocRow = static_cast< SCROW >( nFirstRealDocRowS ); + SCCOL nLastRealDocCol = static_cast< SCCOL >( nLastRealDocColS ); + SCROW nLastRealDocRow = static_cast< SCROW >( nLastRealDocRowS ); + + // first visible column (nX1-1 is first processed document column) + SCCOL nFirstDocCol = (nX1 > 0) ? ::std::max< SCCOL >( nFirstRealDocCol, nX1 - 1 ) : nFirstRealDocCol; + USHORT nFirstCellInfoX = static_cast< USHORT >( nFirstDocCol + 1 ); + nFirstCol = static_cast< size_t >( nFirstCellInfoX - nX1 ); + + // last visible column (nX2+1 is last processed document column) + SCCOL nLastDocCol = (nX2 < MAXCOL) ? ::std::min< SCCOL >( nLastRealDocCol, nX2 + 1 ) : nLastRealDocCol; + USHORT nLastCellInfoX = static_cast< USHORT >( nLastDocCol + 1 ); + size_t nLastCol = static_cast< size_t >( nLastCellInfoX - nX1 ); + + // first visible row + USHORT nFirstCellInfoY = nCellInfoY; + while( ((nFirstCellInfoY > 1) && (pRowInfo[ nFirstCellInfoY - 1 ].nRowNo >= nFirstRealDocRow)) || + ((nFirstCellInfoY == 1) && (static_cast< SCROW >( nY1 - 1 ) >= nFirstRealDocRow)) ) + --nFirstCellInfoY; + SCROW nFirstDocRow = (nFirstCellInfoY > 0) ? pRowInfo[ nFirstCellInfoY ].nRowNo : static_cast< SCROW >( nY1 - 1 ); + nFirstRow = static_cast< size_t >( nFirstCellInfoY ); + + // last visible row + USHORT nLastCellInfoY = nCellInfoY; + while( (sal::static_int_cast<SCSIZE>(nLastCellInfoY + 1) < nArrCount) && + (pRowInfo[ nLastCellInfoY + 1 ].nRowNo <= nLastRealDocRow) ) + ++nLastCellInfoY; + SCROW nLastDocRow = (nLastCellInfoY > 0) ? pRowInfo[ nLastCellInfoY ].nRowNo : static_cast< SCROW >( nY1 - 1 ); + size_t nLastRow = static_cast< size_t >( nLastCellInfoY ); + + // insert merged range + rArray.SetMergedRange( nFirstCol, nFirstRow, nLastCol, nLastRow ); + + // *** find additional size not included in svx::frame::Array *** + + // additional space before first column + if( nFirstCol == 0 ) + { + long nSize = 0; + for( SCCOL nDocCol = nFirstRealDocCol; nDocCol < nFirstDocCol; ++nDocCol ) + nSize += std::max( static_cast< long >( GetColWidth( nDocCol, nTab ) * nScaleX ), 1L ); + rArray.SetAddMergedLeftSize( nCol, nRow, nSize ); + } + // additional space after last column + if( nLastCol + 1 == nColCount ) + { + long nSize = 0; + for( SCCOL nDocCol = nLastDocCol + 1; nDocCol <= nLastRealDocCol; ++nDocCol ) + nSize += std::max( static_cast< long >( GetColWidth( nDocCol, nTab ) * nScaleX ), 1L ); + rArray.SetAddMergedRightSize( nCol, nRow, nSize ); + } + // additional space above first row + if( nFirstRow == 0 ) + { + long nSize = 0; + for( SCROW nDocRow = nFirstRealDocRow; nDocRow < nFirstDocRow; ++nDocRow ) + nSize += std::max( static_cast< long >( GetRowHeight( nDocRow, nTab ) * nScaleY ), 1L ); + rArray.SetAddMergedTopSize( nCol, nRow, nSize ); + } + // additional space beyond last row + if( nLastRow + 1 == nRowCount ) + { + long nSize = 0; + for( SCROW nDocRow = nLastDocRow + 1; nDocRow <= nLastRealDocRow; ++nDocRow ) + nSize += std::max( static_cast< long >( GetRowHeight( nDocRow, nTab ) * nScaleY ), 1L ); + rArray.SetAddMergedBottomSize( nCol, nRow, nSize ); + } + + // *** use line attributes from real origin cell *** + + if( (nFirstRealDocCol != nCurrDocCol) || (nFirstRealDocRow != nCurrDocRow) ) + { + if( const ScPatternAttr* pPattern = GetPattern( nFirstRealDocCol, nFirstRealDocRow, nTab ) ) + { + const SfxItemSet* pCond = GetCondResult( nFirstRealDocCol, nFirstRealDocRow, nTab ); + pBox = static_cast< const SvxBoxItem* >( &pPattern->GetItem( ATTR_BORDER, pCond ) ); + pTLBR = static_cast< const SvxLineItem* >( &pPattern->GetItem( ATTR_BORDER_TLBR, pCond ) ); + pBLTR = static_cast< const SvxLineItem* >( &pPattern->GetItem( ATTR_BORDER_BLTR, pCond ) ); + } + else + { + pBox = 0; + pTLBR = pBLTR = 0; + } + } + } + + // *** borders *** ------------------------------------------------ + + if( pBox ) + { + rArray.SetCellStyleLeft( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetLeft(), nScaleX ) ); + rArray.SetCellStyleRight( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetRight(), nScaleX ) ); + rArray.SetCellStyleTop( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetTop(), nScaleY ) ); + rArray.SetCellStyleBottom( nFirstCol, nFirstRow, svx::frame::Style( pBox->GetBottom(), nScaleY ) ); + } + + if( pTLBR ) + rArray.SetCellStyleTLBR( nFirstCol, nFirstRow, svx::frame::Style( pTLBR->GetLine(), nScaleY ) ); + if( rInfo.mpBLTRLine ) + rArray.SetCellStyleBLTR( nFirstCol, nFirstRow, svx::frame::Style( pBLTR->GetLine(), nScaleY ) ); + } + } + + /* Mirror the entire frame array. + 1st param = Mirror the vertical double line styles as well. + 2nd param = Do not swap diagonal lines. + */ + if( bLayoutRTL ) + rArray.MirrorSelfX( true, false ); +} + +// ============================================================================ + +ScTableInfo::ScTableInfo() : + mpRowInfo( new RowInfo[ ROWINFO_MAX ] ), + mbPageMode( false ) +{ + for( USHORT nIdx = 0; nIdx < ROWINFO_MAX; ++nIdx ) + mpRowInfo[ nIdx ].pCellInfo = 0; +} + +ScTableInfo::~ScTableInfo() +{ + for( USHORT nIdx = 0; nIdx < ROWINFO_MAX; ++nIdx ) + delete [] mpRowInfo[ nIdx ].pCellInfo; + delete [] mpRowInfo; +} + +// ============================================================================ + diff --git a/sc/source/core/data/global.cxx b/sc/source/core/data/global.cxx new file mode 100644 index 000000000000..88ff065572b4 --- /dev/null +++ b/sc/source/core/data/global.cxx @@ -0,0 +1,2010 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: global.cxx,v $ + * $Revision: 1.56.102.1 $ + * + * 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 <vcl/svapp.hxx> +#include "scitems.hxx" +#include <svx/algitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/editobj.hxx> +#include <svx/scripttypeitem.hxx> +#include <svx/srchitem.hxx> +#include <svx/langitem.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/dispatch.hxx> +#include <sfx2/objsh.hxx> +#include <sfx2/viewfrm.hxx> +#include <sfx2/viewsh.hxx> +#include <svtools/stritem.hxx> +#include <svtools/zforlist.hxx> +#include <svtools/zformat.hxx> +#include <vcl/image.hxx> +#include <vcl/virdev.hxx> +#include <tools/rcid.h> +#include <unotools/charclass.hxx> +#include <stdlib.h> +#include <time.h> +#include <ctype.h> +#include <numeric> + + +#include <i18npool/mslangid.hxx> +#include <com/sun/star/lang/Locale.hpp> +#include <comphelper/processfactory.hxx> +#include <unotools/calendarwrapper.hxx> +#include <unotools/collatorwrapper.hxx> +#include <com/sun/star/i18n/CollatorOptions.hpp> +#include <unotools/intlwrapper.hxx> +#include <svtools/syslocale.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "global.hxx" +#include "scresid.hxx" +#include "autoform.hxx" +#include "document.hxx" +#include "patattr.hxx" +#include "addincol.hxx" +#include "adiasync.hxx" +#include "userlist.hxx" +#include "interpre.hxx" +#include "strload.hxx" +#include "docpool.hxx" +#include "unitconv.hxx" +#include "compiler.hxx" +#include "parclass.hxx" +#include "funcdesc.hxx" +#include "globstr.hrc" +#include "scfuncs.hrc" +#include "sc.hrc" +#include "scmod.hxx" +#include "appoptio.hxx" + +// ----------------------------------------------------------------------- + +#define CLIPST_AVAILABLE 0 +#define CLIPST_CAPTURED 1 +#define CLIPST_DELETE 2 +#define CLIPST_DRAW 3 + +ScDocShellRef* ScGlobal::pDrawClipDocShellRef = NULL; +SvxSearchItem* ScGlobal::pSearchItem = NULL; +ScAutoFormat* ScGlobal::pAutoFormat = NULL; +FuncCollection* ScGlobal::pFuncCollection = NULL; +ScUnoAddInCollection* ScGlobal::pAddInCollection = NULL; +ScUserList* ScGlobal::pUserList = NULL; +String** ScGlobal::ppRscString = NULL; +LanguageType ScGlobal::eLnge = LANGUAGE_SYSTEM; +::com::sun::star::lang::Locale* ScGlobal::pLocale = NULL; +SvtSysLocale* ScGlobal::pSysLocale = NULL; +const CharClass* ScGlobal::pCharClass = NULL; +const LocaleDataWrapper* ScGlobal::pLocaleData = NULL; +CalendarWrapper* ScGlobal::pCalendar = NULL; +CollatorWrapper* ScGlobal::pCollator = NULL; +CollatorWrapper* ScGlobal::pCaseCollator = NULL; +::utl::TransliterationWrapper* ScGlobal::pTransliteration = NULL; +::utl::TransliterationWrapper* ScGlobal::pCaseTransliteration = NULL; +::com::sun::star::uno::Reference< ::com::sun::star::i18n::XOrdinalSuffix> ScGlobal::xOrdinalSuffix = NULL; +IntlWrapper* ScGlobal::pScIntlWrapper = NULL; +sal_Unicode ScGlobal::cListDelimiter = ','; +String* ScGlobal::pEmptyString = NULL; +String* ScGlobal::pStrClipDocName = NULL; + +SvxBrushItem* ScGlobal::pEmptyBrushItem = NULL; +SvxBrushItem* ScGlobal::pButtonBrushItem = NULL; +SvxBrushItem* ScGlobal::pEmbeddedBrushItem = NULL; +SvxBrushItem* ScGlobal::pProtectedBrushItem = NULL; + +ImageList* ScGlobal::pOutlineBitmaps = NULL; +ImageList* ScGlobal::pOutlineBitmapsHC = NULL; + +ScFunctionList* ScGlobal::pStarCalcFunctionList = NULL; +ScFunctionMgr* ScGlobal::pStarCalcFunctionMgr = NULL; + +ScUnitConverter* ScGlobal::pUnitConverter = NULL; +SvNumberFormatter* ScGlobal::pEnglishFormatter = NULL; + +double ScGlobal::nScreenPPTX = 96.0; +double ScGlobal::nScreenPPTY = 96.0; + +USHORT ScGlobal::nDefFontHeight = 240; +USHORT ScGlobal::nStdRowHeight = 257; + +long ScGlobal::nLastRowHeightExtra = 0; +long ScGlobal::nLastColWidthExtra = STD_EXTRA_WIDTH; + +static USHORT nPPTZoom = 0; // ScreenZoom used to determine nScreenPPTX/Y + + +// ... oder so? + +BOOL bOderSo; + +bool SC_DLLPUBLIC ScGetWriteTeamInfo() +{ + return bOderSo; +} + +class SfxViewShell; +SfxViewShell* pScActiveViewShell = NULL; //! als Member !!!!! +USHORT nScClickMouseModifier = 0; //! dito +USHORT nScFillModeMouseModifier = 0; //! dito + +// Hack: ScGlobal::GetUserList() muss InitAppOptions in der UI aufrufen, +// damit UserList aus Cfg geladen wird + +void global_InitAppOptions(); + +//======================================================================== +// +// statische Funktionen +// +//======================================================================== + +BOOL ScGlobal::HasAttrChanged( const SfxItemSet& rNewAttrs, + const SfxItemSet& rOldAttrs, + const USHORT nWhich ) +{ + BOOL bInvalidate = FALSE; + const SfxItemState eNewState = rNewAttrs.GetItemState( nWhich ); + const SfxItemState eOldState = rOldAttrs.GetItemState( nWhich ); + + //---------------------------------------------------------- + + if ( eNewState == eOldState ) + { + // beide Items gesetzt + // PoolItems, d.h. Pointer-Vergleich zulaessig + if ( SFX_ITEM_SET == eOldState ) + bInvalidate = (&rNewAttrs.Get( nWhich ) != &rOldAttrs.Get( nWhich )); + } + else + { + // ein Default-Item dabei + // PoolItems, d.h. Item-Vergleich noetig + + const SfxPoolItem& rOldItem = ( SFX_ITEM_SET == eOldState ) + ? rOldAttrs.Get( nWhich ) + : rOldAttrs.GetPool()->GetDefaultItem( nWhich ); + + const SfxPoolItem& rNewItem = ( SFX_ITEM_SET == eNewState ) + ? rNewAttrs.Get( nWhich ) + : rNewAttrs.GetPool()->GetDefaultItem( nWhich ); + + bInvalidate = sal::static_int_cast<BOOL>(rNewItem != rOldItem); + } + + return bInvalidate; +} + +ULONG ScGlobal::GetStandardFormat( SvNumberFormatter& rFormatter, + ULONG nFormat, short nType ) +{ + const SvNumberformat* pFormat = rFormatter.GetEntry( nFormat ); + if ( pFormat ) + return rFormatter.GetStandardFormat( nFormat, nType, pFormat->GetLanguage() ); + return rFormatter.GetStandardFormat( nType, eLnge ); +} + +ULONG ScGlobal::GetStandardFormat( double fNumber, SvNumberFormatter& rFormatter, + ULONG nFormat, short nType ) +{ + const SvNumberformat* pFormat = rFormatter.GetEntry( nFormat ); + if ( pFormat ) + return rFormatter.GetStandardFormat( fNumber, nFormat, nType, + pFormat->GetLanguage() ); + return rFormatter.GetStandardFormat( nType, eLnge ); +} + + +// static +SvNumberFormatter* ScGlobal::GetEnglishFormatter() +{ + if ( !pEnglishFormatter ) + { + pEnglishFormatter = new SvNumberFormatter( + ::comphelper::getProcessServiceFactory(), LANGUAGE_ENGLISH_US ); + pEnglishFormatter->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT ); + } + return pEnglishFormatter; +} + + +//------------------------------------------------------------------------ + +BOOL ScGlobal::CheckWidthInvalidate( BOOL& bNumFormatChanged, + const SfxItemSet& rNewAttrs, + const SfxItemSet& rOldAttrs ) +{ + // Ueberpruefen, ob Attributaenderungen in rNewAttrs gegnueber + // rOldAttrs die Textbreite an einer Zelle ungueltig machen + + bNumFormatChanged = + HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_VALUE_FORMAT ); + return ( bNumFormatChanged + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LANGUAGE_FORMAT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_HEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_HEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_HEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_WEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_WEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_WEIGHT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_POSTURE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CJK_FONT_POSTURE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_CTL_FONT_POSTURE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_UNDERLINE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_OVERLINE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CROSSEDOUT ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_CONTOUR ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_FONT_SHADOWED ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_STACKED ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_VALUE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_ROTATE_MODE ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_LINEBREAK ) + || HasAttrChanged( rNewAttrs, rOldAttrs, ATTR_MARGIN ) + ); +} + +const SvxSearchItem& ScGlobal::GetSearchItem() +{ + if (!pSearchItem) + { + pSearchItem = new SvxSearchItem( SID_SEARCH_ITEM ); + pSearchItem->SetAppFlag( SVX_SEARCHAPP_CALC ); + } + return *pSearchItem; +} + +void ScGlobal::SetSearchItem( const SvxSearchItem& rNew ) +{ + // Hier waere ein Zuweisungsoperator ganz nett: + delete pSearchItem; + pSearchItem = (SvxSearchItem*)rNew.Clone(); + + pSearchItem->SetWhich( SID_SEARCH_ITEM ); +} + +void ScGlobal::ClearAutoFormat() +{ + if (pAutoFormat!=NULL) + { + delete pAutoFormat; + pAutoFormat=NULL; + } +} + +ScAutoFormat* ScGlobal::GetAutoFormat() +{ + if ( !pAutoFormat ) + { + pAutoFormat = new ScAutoFormat; + pAutoFormat->Load(); + } + + return pAutoFormat; +} + +FuncCollection* ScGlobal::GetFuncCollection() +{ + if (!pFuncCollection) + pFuncCollection = new FuncCollection(); + return pFuncCollection; +} + +ScUnoAddInCollection* ScGlobal::GetAddInCollection() +{ + if (!pAddInCollection) + pAddInCollection = new ScUnoAddInCollection(); + return pAddInCollection; +} + +ScUserList* ScGlobal::GetUserList() +{ + // Hack: Cfg-Item an der App ggF. laden + + global_InitAppOptions(); + + if (!pUserList) + pUserList = new ScUserList(); + return pUserList; +} + +void ScGlobal::SetUserList( const ScUserList* pNewList ) +{ + if ( pNewList ) + { + if ( !pUserList ) + pUserList = new ScUserList( *pNewList ); + else + *pUserList = *pNewList; + } + else + { + delete pUserList; + pUserList = NULL; + } +} + +const String& ScGlobal::GetRscString( USHORT nIndex ) +{ + DBG_ASSERT( nIndex < STR_COUNT, "ScGlobal::GetRscString - invalid string index"); + if( !ppRscString[ nIndex ] ) + { + OpCode eOp = ocNone; + // Map former globstr.src strings moved to compiler.src + switch (nIndex) + { + case STR_NULL_ERROR: + eOp = ocErrNull; + break; + case STR_DIV_ZERO: + eOp = ocErrDivZero; + break; + case STR_NO_VALUE: + eOp = ocErrValue; + break; + case STR_NOREF_STR: + eOp = ocErrRef; + break; + case STR_NO_NAME_REF: + eOp = ocErrName; + break; + case STR_NUM_ERROR: + eOp = ocErrNum; + break; + case STR_NV_STR: + eOp = ocErrNA; + break; + default: + ; // nothing + } + if (eOp != ocNone) + ppRscString[ nIndex ] = new String( + ScCompiler::GetNativeSymbol( eOp)); + else + ppRscString[ nIndex ] = new String( + ScRscStrLoader( RID_GLOBSTR, nIndex ).GetString()); + } + return *ppRscString[ nIndex ]; +} + +String ScGlobal::GetErrorString(USHORT nErrNumber) +{ + String sResStr; + switch (nErrNumber) + { + case NOTAVAILABLE : nErrNumber = STR_NV_STR; break; + case errNoRef : nErrNumber = STR_NO_REF_TABLE; break; + case errNoName : nErrNumber = STR_NO_NAME_REF; break; + case errNoAddin : nErrNumber = STR_NO_ADDIN; break; + case errNoMacro : nErrNumber = STR_NO_MACRO; break; + case errDoubleRef : + case errNoValue : nErrNumber = STR_NO_VALUE; break; + case errNoCode : nErrNumber = STR_NULL_ERROR; break; + case errDivisionByZero : nErrNumber = STR_DIV_ZERO; break; + case errIllegalFPOperation : nErrNumber = STR_NUM_ERROR; break; + + default : sResStr = GetRscString(STR_ERROR_STR); + sResStr += String::CreateFromInt32( nErrNumber ); + nErrNumber = 0; + break; + } + if( nErrNumber ) + sResStr = GetRscString( nErrNumber ); + return sResStr; +} + +String ScGlobal::GetLongErrorString(USHORT nErrNumber) +{ + switch (nErrNumber) + { + case 0: + break; + case 1: + case errIllegalArgument: + nErrNumber = STR_LONG_ERR_ILL_ARG; + break; + case 2: + case 3: + case 4: + case 5: + case errIllegalFPOperation: + nErrNumber = STR_LONG_ERR_ILL_FPO; + break; + case errIllegalChar: + nErrNumber = STR_LONG_ERR_ILL_CHAR; + break; + case errIllegalParameter: + nErrNumber = STR_LONG_ERR_ILL_PAR; + break; + case errSeparator: + nErrNumber = STR_LONG_ERR_ILL_SEP; + break; + case errPair: + case errPairExpected: + nErrNumber = STR_LONG_ERR_PAIR; + break; + case errOperatorExpected: + nErrNumber = STR_LONG_ERR_OP_EXP; + break; + case errVariableExpected: + case errParameterExpected: + nErrNumber = STR_LONG_ERR_VAR_EXP; + break; + case errCodeOverflow: + nErrNumber = STR_LONG_ERR_CODE_OVF; + break; + case errStringOverflow: + nErrNumber = STR_LONG_ERR_STR_OVF; + break; + case errStackOverflow: + case errInterpOverflow: + nErrNumber = STR_LONG_ERR_STACK_OVF; + break; + case errIllegalJump: + case errUnknownState: + case errUnknownVariable: + case errUnknownOpCode: + case errUnknownStackVariable: + case errUnknownToken: + case errNoCode: + case errDoubleRef: + nErrNumber = STR_LONG_ERR_SYNTAX; + break; + case errCircularReference: + nErrNumber = STR_LONG_ERR_CIRC_REF; + break; + case errNoConvergence: + nErrNumber = STR_LONG_ERR_NO_CONV; + break; + case errNoRef: + nErrNumber = STR_LONG_ERR_NO_REF; + break; + case errNoName: + nErrNumber = STR_LONG_ERR_NO_NAME; + break; + case errNoAddin: + nErrNumber = STR_LONG_ERR_NO_ADDIN; + break; + case errNoMacro: + nErrNumber = STR_LONG_ERR_NO_MACRO; + break; + case errDivisionByZero: + nErrNumber = STR_LONG_ERR_DIV_ZERO; + break; + case errNestedArray: + nErrNumber = STR_ERR_LONG_NESTED_ARRAY; + break; + case errNoValue: + nErrNumber = STR_LONG_ERR_NO_VALUE; + break; + case NOTAVAILABLE: + nErrNumber = STR_LONG_ERR_NV; + break; + default: + nErrNumber = STR_ERROR_STR; + break; + } + String aRes( GetRscString( nErrNumber ) ); + if( bOderSo ) + { + String aOderSo( GetRscString( STR_ODER_SO ) ); + aOderSo.SearchAndReplace( String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("%s")), aRes ); + aRes = aOderSo; + } + return aRes; +} + +SvxBrushItem* ScGlobal::GetButtonBrushItem() +{ + pButtonBrushItem->SetColor( Application::GetSettings().GetStyleSettings().GetFaceColor() ); + return pButtonBrushItem; +} + +const String& ScGlobal::GetEmptyString() +{ + return *pEmptyString; +} + +ImageList* ScGlobal::GetOutlineSymbols( bool bHC ) +{ + ImageList*& rpImageList = bHC ? pOutlineBitmapsHC : pOutlineBitmaps; + if( !rpImageList ) + rpImageList = new ImageList( ScResId( bHC ? RID_OUTLINEBITMAPS_H : RID_OUTLINEBITMAPS ) ); + return rpImageList; +} + +void ScGlobal::Init() +{ + pEmptyString = new String; + + // Die Default-Sprache fuer Zahlenformate (ScGlobal::eLnge) + // muss immer LANGUAGE_SYSTEM sein + //! Dann kann auch die Variable raus + eLnge = LANGUAGE_SYSTEM; + + //! Wenn Sortierung etc. von der Sprache der installierten Offfice-Version + //! abhaengen sollen, hier "Application::GetSettings().GetUILanguage()" + pSysLocale = new SvtSysLocale; + pCharClass = pSysLocale->GetCharClassPtr(); + pLocaleData = pSysLocale->GetLocaleDataPtr(); + + ppRscString = new String *[ STR_COUNT ]; + for( USHORT nC = 0 ; nC < STR_COUNT ; nC++ ) ppRscString[ nC ] = NULL; + + pEmptyBrushItem = new SvxBrushItem( Color( COL_TRANSPARENT ), ATTR_BACKGROUND ); + pButtonBrushItem = new SvxBrushItem( Color(), ATTR_BACKGROUND ); + pEmbeddedBrushItem = new SvxBrushItem( Color( COL_LIGHTCYAN ), ATTR_BACKGROUND ); + pProtectedBrushItem = new SvxBrushItem( Color( COL_LIGHTGRAY ), ATTR_BACKGROUND ); + + UpdatePPT(NULL); + //ScCompiler::InitSymbolsNative(); + // ScParameterClassification _after_ Compiler, needs function resources if + // arguments are to be merged in, which in turn need strings of function + // names from the compiler. + ScParameterClassification::Init(); + srand( (unsigned) time( NULL ) ); // Random Seed Init fuer Interpreter + + InitAddIns(); + + pStrClipDocName = new String( ScResId( SCSTR_NONAME ) ); + *pStrClipDocName += '1'; + + // ScDocumentPool::InitVersionMaps() ist schon vorher gerufen worden +} + +void ScGlobal::UpdatePPT( OutputDevice* pDev ) +{ + USHORT nCurrentZoom = Application::GetSettings().GetStyleSettings().GetScreenZoom(); + if ( nCurrentZoom != nPPTZoom ) + { + // Screen PPT values must be updated when ScreenZoom has changed. + // If called from Window::DataChanged, the window is passed as pDev, + // to make sure LogicToPixel uses a device which already uses the new zoom. + // For the initial settings, NULL is passed and GetDefaultDevice used. + + if ( !pDev ) + pDev = Application::GetDefaultDevice(); + Point aPix1000 = pDev->LogicToPixel( Point(1000,1000), MAP_TWIP ); + nScreenPPTX = aPix1000.X() / 1000.0; + nScreenPPTY = aPix1000.Y() / 1000.0; + nPPTZoom = nCurrentZoom; + } +} + +const String& ScGlobal::GetClipDocName() +{ + return *pStrClipDocName; +} + +void ScGlobal::SetClipDocName( const String& rNew ) +{ + *pStrClipDocName = rNew; +} + + +void ScGlobal::InitTextHeight(SfxItemPool* pPool) +{ + if (!pPool) + { + DBG_ERROR("kein Pool bei ScGlobal::InitTextHeight"); + return; + } + + const ScPatternAttr* pPattern = (const ScPatternAttr*)&pPool->GetDefaultItem(ATTR_PATTERN); + if (!pPattern) + { + DBG_ERROR("kein Default-Pattern bei ScGlobal::InitTextHeight"); + return; + } + +// String aTestString('X'); + OutputDevice* pDefaultDev = Application::GetDefaultDevice(); + VirtualDevice aVirtWindow( *pDefaultDev ); + aVirtWindow.SetMapMode(MAP_PIXEL); + Font aDefFont; + pPattern->GetFont(aDefFont, SC_AUTOCOL_BLACK, &aVirtWindow); // font color doesn't matter here + aVirtWindow.SetFont(aDefFont); + nDefFontHeight = (USHORT) aVirtWindow.PixelToLogic(Size(0, aVirtWindow.GetTextHeight()), + MAP_TWIP).Height(); + + const SvxMarginItem* pMargin = (const SvxMarginItem*)&pPattern->GetItem(ATTR_MARGIN); + + nStdRowHeight = (USHORT) ( nDefFontHeight + + pMargin->GetTopMargin() + pMargin->GetBottomMargin() + - STD_ROWHEIGHT_DIFF ); +} + +void ScGlobal::Clear() +{ + // asyncs _vor_ ExitExternalFunc zerstoeren! + theAddInAsyncTbl.DeleteAndDestroy( 0, theAddInAsyncTbl.Count() ); + ExitExternalFunc(); + DELETEZ(pAutoFormat); + DELETEZ(pSearchItem); + DELETEZ(pFuncCollection); + DELETEZ(pAddInCollection); + DELETEZ(pUserList); + + for( USHORT nC = 0 ; nC < STR_COUNT ; nC++ ) + if( ppRscString ) delete ppRscString[ nC ]; + delete[] ppRscString; + ppRscString = NULL; + + DELETEZ(pStarCalcFunctionList); // vor ResMgr zerstoeren! + DELETEZ(pStarCalcFunctionMgr); + ScParameterClassification::Exit(); + ScCompiler::DeInit(); + ScInterpreter::GlobalExit(); // statischen Stack loeschen + + DELETEZ(pEmptyBrushItem); + DELETEZ(pButtonBrushItem); + DELETEZ(pEmbeddedBrushItem); + DELETEZ(pProtectedBrushItem); + DELETEZ(pOutlineBitmaps); + DELETEZ(pOutlineBitmapsHC); +// DELETEZ(pAnchorBitmap); +// DELETEZ(pGrayAnchorBitmap); + DELETEZ(pEnglishFormatter); + DELETEZ(pCaseTransliteration); + DELETEZ(pTransliteration); + DELETEZ(pCaseCollator); + DELETEZ(pCollator); + DELETEZ(pCalendar); + //! do NOT delete pCharClass since it is a pointer to the single SvtSysLocale instance + pCharClass = NULL; + //! do NOT delete pLocaleData since it is a pointer to the single SvtSysLocale instance + pLocaleData = NULL; + DELETEZ(pSysLocale); + DELETEZ(pLocale); + DELETEZ(pScIntlWrapper); + DELETEZ(pStrClipDocName); + + DELETEZ(pUnitConverter); + + ScDocumentPool::DeleteVersionMaps(); + + DELETEZ(pEmptyString); +} + +//------------------------------------------------------------------------ + +// static +CharSet ScGlobal::GetCharsetValue( const String& rCharSet ) +{ + // new TextEncoding values + if ( CharClass::isAsciiNumeric( rCharSet ) ) + { + sal_Int32 nVal = rCharSet.ToInt32(); + if ( !nVal || nVal == RTL_TEXTENCODING_DONTKNOW ) + return gsl_getSystemTextEncoding(); + return (CharSet) nVal; + } + // old CharSet values for compatibility + else if (rCharSet.EqualsIgnoreCaseAscii("ANSI") ) return RTL_TEXTENCODING_MS_1252; + else if (rCharSet.EqualsIgnoreCaseAscii("MAC") ) return RTL_TEXTENCODING_APPLE_ROMAN; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC") ) return RTL_TEXTENCODING_IBM_850; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_437")) return RTL_TEXTENCODING_IBM_437; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_850")) return RTL_TEXTENCODING_IBM_850; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_860")) return RTL_TEXTENCODING_IBM_860; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_861")) return RTL_TEXTENCODING_IBM_861; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_863")) return RTL_TEXTENCODING_IBM_863; + else if (rCharSet.EqualsIgnoreCaseAscii("IBMPC_865")) return RTL_TEXTENCODING_IBM_865; +// else if (rCharSet.EqualsIgnoreCaseAscii("SYSTEM") ) return gsl_getSystemTextEncoding(); + else return gsl_getSystemTextEncoding(); +} + +//------------------------------------------------------------------------ + +// static +String ScGlobal::GetCharsetString( CharSet eVal ) +{ + const sal_Char* pChar; + switch ( eVal ) + { + // old CharSet strings for compatibility + case RTL_TEXTENCODING_MS_1252: pChar = "ANSI"; break; + case RTL_TEXTENCODING_APPLE_ROMAN: pChar = "MAC"; break; + // IBMPC == IBMPC_850 + case RTL_TEXTENCODING_IBM_437: pChar = "IBMPC_437"; break; + case RTL_TEXTENCODING_IBM_850: pChar = "IBMPC_850"; break; + case RTL_TEXTENCODING_IBM_860: pChar = "IBMPC_860"; break; + case RTL_TEXTENCODING_IBM_861: pChar = "IBMPC_861"; break; + case RTL_TEXTENCODING_IBM_863: pChar = "IBMPC_863"; break; + case RTL_TEXTENCODING_IBM_865: pChar = "IBMPC_865"; break; + case RTL_TEXTENCODING_DONTKNOW: pChar = "SYSTEM"; break; + // new string of TextEncoding value + default: + return String::CreateFromInt32( eVal ); + } + return String::CreateFromAscii(pChar); +} + +//------------------------------------------------------------------------ + +bool ScGlobal::HasStarCalcFunctionList() +{ + return ( pStarCalcFunctionList != NULL ); +} + +ScFunctionList* ScGlobal::GetStarCalcFunctionList() +{ + if ( !pStarCalcFunctionList ) + pStarCalcFunctionList = new ScFunctionList; + + return pStarCalcFunctionList; +} + +//------------------------------------------------------------------------ + +ScFunctionMgr* ScGlobal::GetStarCalcFunctionMgr() +{ + if ( !pStarCalcFunctionMgr ) + pStarCalcFunctionMgr = new ScFunctionMgr; + + return pStarCalcFunctionMgr; +} + +void ScGlobal::ResetFunctionList() +{ + // FunctionMgr has pointers into FunctionList, must also be updated + + DELETEZ( pStarCalcFunctionMgr ); + DELETEZ( pStarCalcFunctionList ); +} + +//------------------------------------------------------------------------ + +// static +ScUnitConverter* ScGlobal::GetUnitConverter() +{ + if ( !pUnitConverter ) + pUnitConverter = new ScUnitConverter; + + return pUnitConverter; +} + + +//------------------------------------------------------------------------ + +// static +const sal_Unicode* ScGlobal::UnicodeStrChr( const sal_Unicode* pStr, + sal_Unicode c ) +{ + if ( !pStr ) + return NULL; + while ( *pStr ) + { + if ( *pStr == c ) + return pStr; + pStr++; + } + return NULL; +} + +// ---------------------------------------------------------------------------- + +void ScGlobal::AddToken( String& rTokenList, const String& rToken, sal_Unicode cSep, xub_StrLen nSepCount, bool bForceSep ) +{ + if( bForceSep || (rToken.Len() && rTokenList.Len()) ) + rTokenList.Expand( rTokenList.Len() + nSepCount, cSep ); + rTokenList.Append( rToken ); +} + +bool ScGlobal::IsQuoted( const String& rString, sal_Unicode cQuote ) +{ + return (rString.Len() >= 2) && (rString.GetChar( 0 ) == cQuote) && (rString.GetChar( rString.Len() - 1 ) == cQuote); +} + +void ScGlobal::AddQuotes( String& rString, sal_Unicode cQuote, bool bEscapeEmbedded ) +{ + if (bEscapeEmbedded) + { + sal_Unicode pQ[3]; + pQ[0] = pQ[1] = cQuote; + pQ[2] = 0; + String aQuotes( pQ ); + rString.SearchAndReplaceAll( cQuote, aQuotes); + } + rString.Insert( cQuote, 0 ).Append( cQuote ); +} + +void ScGlobal::EraseQuotes( String& rString, sal_Unicode cQuote, bool bUnescapeEmbedded ) +{ + if ( IsQuoted( rString, cQuote ) ) + { + rString.Erase( rString.Len() - 1 ).Erase( 0, 1 ); + if (bUnescapeEmbedded) + { + sal_Unicode pQ[3]; + pQ[0] = pQ[1] = cQuote; + pQ[2] = 0; + String aQuotes( pQ ); + rString.SearchAndReplaceAll( aQuotes, cQuote); + } + } +} + +xub_StrLen ScGlobal::FindUnquoted( const String& rString, sal_Unicode cChar, xub_StrLen nStart, sal_Unicode cQuote ) +{ + const sal_Unicode* const pStart = rString.GetBuffer(); + const sal_Unicode* const pStop = pStart + rString.Len(); + const sal_Unicode* p = pStart + nStart; + bool bQuoted = false; + while (p < pStop) + { + if (*p == cChar && !bQuoted) + return sal::static_int_cast< xub_StrLen >( p - pStart ); + else if (*p == cQuote) + { + if (!bQuoted) + bQuoted = true; + else if (p < pStop-1 && *(p+1) == cQuote) + ++p; + else + bQuoted = false; + } + ++p; + } + return STRING_NOTFOUND; +} + +const sal_Unicode* ScGlobal::FindUnquoted( const sal_Unicode* pString, sal_Unicode cChar, sal_Unicode cQuote ) +{ + const sal_Unicode* p = pString; + bool bQuoted = false; + while (*p) + { + if (*p == cChar && !bQuoted) + return p; + else if (*p == cQuote) + { + if (!bQuoted) + bQuoted = true; + else if (*(p+1) == cQuote) + ++p; + else + bQuoted = false; + } + ++p; + } + return NULL; +} + +//------------------------------------------------------------------------ + +BOOL ScGlobal::EETextObjEqual( const EditTextObject* pObj1, + const EditTextObject* pObj2 ) +{ + if ( pObj1 == pObj2 ) // both empty or the same object + return TRUE; + + if ( pObj1 && pObj2 ) + { + // first test for equal text content + USHORT nParCount = pObj1->GetParagraphCount(); + if ( nParCount != pObj2->GetParagraphCount() ) + return FALSE; + for (USHORT nPar=0; nPar<nParCount; nPar++) + if ( pObj1->GetText(nPar) != pObj2->GetText(nPar) ) + return FALSE; + + SvMemoryStream aStream1; + SvMemoryStream aStream2; + pObj1->Store( aStream1 ); + pObj2->Store( aStream2 ); + ULONG nSize = aStream1.Tell(); + if ( aStream2.Tell() == nSize ) + if ( !memcmp( aStream1.GetData(), aStream2.GetData(), (USHORT) nSize ) ) + return TRUE; + } + + return FALSE; +} + +void ScGlobal::OpenURL( const String& rURL, const String& rTarget ) +{ + // OpenURL wird immer ueber irgendwelche Umwege durch Mausklicks im GridWindow + // aufgerufen, darum stimmen pScActiveViewShell und nScClickMouseModifier. + + SfxStringItem aUrl( SID_FILE_NAME, rURL ); + SfxStringItem aTarget( SID_TARGETNAME, rTarget ); + + if ( nScClickMouseModifier & KEY_MOD1 ) // control-click -> into new window + aTarget.SetValue( + String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM("_blank")) ); + + SfxViewFrame* pFrame = NULL; + String aReferName; + if ( pScActiveViewShell ) + { + pFrame = pScActiveViewShell->GetViewFrame(); + SfxMedium* pMed = pFrame->GetObjectShell()->GetMedium(); + if (pMed) + aReferName = pMed->GetName(); + } + + SfxFrameItem aFrm( SID_DOCFRAME, pFrame ); + SfxStringItem aReferer( SID_REFERER, aReferName ); + + SfxBoolItem aNewView( SID_OPEN_NEW_VIEW, FALSE ); + SfxBoolItem aBrowsing( SID_BROWSE, TRUE ); + + // kein SID_SILENT mehr wegen Bug #42525# (war angeblich sowieso falsch) + + SfxViewFrame* pViewFrm = SfxViewFrame::Current(); + if (pViewFrm) + pViewFrm->GetDispatcher()->Execute( SID_OPENDOC, + SFX_CALLMODE_ASYNCHRON | SFX_CALLMODE_RECORD, + &aUrl, &aTarget, + &aFrm, &aReferer, + &aNewView, &aBrowsing, + 0L ); +} + +//------------------------------------------------------------------------ + +BOOL ScGlobal::IsSystemRTL() +{ + return MsLangId::isRightToLeft( Application::GetSettings().GetLanguage() ); +} + +BYTE ScGlobal::GetDefaultScriptType() +{ + // Used when text contains only WEAK characters. + // Script type of office language is used then (same as GetEditDefaultLanguage, + // to get consistent behavior of text in simple cells and EditEngine, + // also same as GetAppLanguage() in Writer) + + return (BYTE) SvtLanguageOptions::GetScriptTypeOfLanguage( Application::GetSettings().GetLanguage() ); +} + +LanguageType ScGlobal::GetEditDefaultLanguage() +{ + // used for EditEngine::SetDefaultLanguage + + return Application::GetSettings().GetLanguage(); +} + +USHORT ScGlobal::GetScriptedWhichID( BYTE nScriptType, USHORT nWhich ) +{ + switch ( nScriptType ) + { + case SCRIPTTYPE_LATIN: + case SCRIPTTYPE_ASIAN: + case SCRIPTTYPE_COMPLEX: + break; // take exact matches + default: // prefer one, first COMPLEX, then ASIAN + if ( nScriptType & SCRIPTTYPE_COMPLEX ) + nScriptType = SCRIPTTYPE_COMPLEX; + else if ( nScriptType & SCRIPTTYPE_ASIAN ) + nScriptType = SCRIPTTYPE_ASIAN; + } + switch ( nScriptType ) + { + case SCRIPTTYPE_COMPLEX: + { + switch ( nWhich ) + { + case ATTR_FONT: + case ATTR_CJK_FONT: + nWhich = ATTR_CTL_FONT; + break; + case ATTR_FONT_HEIGHT: + case ATTR_CJK_FONT_HEIGHT: + nWhich = ATTR_CTL_FONT_HEIGHT; + break; + case ATTR_FONT_WEIGHT: + case ATTR_CJK_FONT_WEIGHT: + nWhich = ATTR_CTL_FONT_WEIGHT; + break; + case ATTR_FONT_POSTURE: + case ATTR_CJK_FONT_POSTURE: + nWhich = ATTR_CTL_FONT_POSTURE; + break; + } + } + break; + case SCRIPTTYPE_ASIAN: + { + switch ( nWhich ) + { + case ATTR_FONT: + case ATTR_CTL_FONT: + nWhich = ATTR_CJK_FONT; + break; + case ATTR_FONT_HEIGHT: + case ATTR_CTL_FONT_HEIGHT: + nWhich = ATTR_CJK_FONT_HEIGHT; + break; + case ATTR_FONT_WEIGHT: + case ATTR_CTL_FONT_WEIGHT: + nWhich = ATTR_CJK_FONT_WEIGHT; + break; + case ATTR_FONT_POSTURE: + case ATTR_CTL_FONT_POSTURE: + nWhich = ATTR_CJK_FONT_POSTURE; + break; + } + } + break; + default: + { + switch ( nWhich ) + { + case ATTR_CTL_FONT: + case ATTR_CJK_FONT: + nWhich = ATTR_FONT; + break; + case ATTR_CTL_FONT_HEIGHT: + case ATTR_CJK_FONT_HEIGHT: + nWhich = ATTR_FONT_HEIGHT; + break; + case ATTR_CTL_FONT_WEIGHT: + case ATTR_CJK_FONT_WEIGHT: + nWhich = ATTR_FONT_WEIGHT; + break; + case ATTR_CTL_FONT_POSTURE: + case ATTR_CJK_FONT_POSTURE: + nWhich = ATTR_FONT_POSTURE; + break; + } + } + } + return nWhich; +} + +//------------------------------------------------------------------------ + +void ScGlobal::AddLanguage( SfxItemSet& rSet, SvNumberFormatter& rFormatter ) +{ + DBG_ASSERT( rSet.GetItemState( ATTR_LANGUAGE_FORMAT, FALSE ) == SFX_ITEM_DEFAULT, + "ScGlobal::AddLanguage - language already added"); + + const SfxPoolItem* pHardItem; + if ( rSet.GetItemState( ATTR_VALUE_FORMAT, FALSE, &pHardItem ) == SFX_ITEM_SET ) + { + const SvNumberformat* pHardFormat = rFormatter.GetEntry( + ((const SfxUInt32Item*)pHardItem)->GetValue() ); + + ULONG nParentFmt = 0; // pool default + const SfxItemSet* pParent = rSet.GetParent(); + if ( pParent ) + nParentFmt = ((const SfxUInt32Item&)pParent->Get( ATTR_VALUE_FORMAT )).GetValue(); + const SvNumberformat* pParFormat = rFormatter.GetEntry( nParentFmt ); + + if ( pHardFormat && pParFormat && + (pHardFormat->GetLanguage() != pParFormat->GetLanguage()) ) + rSet.Put( SvxLanguageItem( pHardFormat->GetLanguage(), ATTR_LANGUAGE_FORMAT ) ); + } +} + + + + + +//=================================================================== +// class ScFunctionList: +//=================================================================== + +//=================================================================== +// class ScFuncRes +// fuer temporaere Objekte zum Holen der Resourcen + +class ScFuncRes : public Resource +{ +public: + ScFuncRes( ResId&, ScFuncDesc*, bool & rbSuppressed ); + +private: + USHORT GetNum(); +}; + +//-------------------------------------------------------------------- + +ScFuncRes::ScFuncRes( ResId &aRes, ScFuncDesc* pDesc, bool & rbSuppressed ) + : Resource(aRes) +{ + rbSuppressed = (bool)GetNum(); + pDesc->nCategory = GetNum(); + pDesc->nHelpId = GetNum() + 32768; //! Hack, see scfuncs.src + pDesc->nArgCount = GetNum(); + USHORT nArgs = pDesc->nArgCount; + if (nArgs >= VAR_ARGS) + nArgs -= VAR_ARGS - 1; + if (nArgs) + { + pDesc->pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgs]; + for (USHORT i = 0; i < nArgs; i++) + { + pDesc->pDefArgFlags[i].bOptional = (bool)GetNum(); + } + } + // Need to read the value from the resource even if nArgs==0 to advance the + // resource position pointer, so this can't be in the if(nArgs) block above. + USHORT nSuppressed = GetNum(); + if (nSuppressed) + { + if (nSuppressed > nArgs) + { + DBG_ERROR3( "ScFuncRes: suppressed parameters count mismatch on OpCode %u: suppressed %d > params %d", + aRes.GetId(), (int)nSuppressed, (int)nArgs); + nSuppressed = nArgs; // sanitize + } + for (USHORT i=0; i < nSuppressed; ++i) + { + USHORT nParam = GetNum(); + if (nParam < nArgs) + { + if (pDesc->nArgCount >= VAR_ARGS && nParam == nArgs-1) + { + DBG_ERROR3( "ScFuncRes: VAR_ARGS parameters can't be suppressed, on OpCode %u: param %d == arg %d-1", + aRes.GetId(), (int)nParam, (int)nArgs); + } + else + { + pDesc->pDefArgFlags[nParam].bSuppress = true; + pDesc->bHasSuppressedArgs = true; + } + } + else + { + DBG_ERROR3( "ScFuncRes: suppressed parameter exceeds count on OpCode %u: param %d >= args %d", + aRes.GetId(), (int)nParam, (int)nArgs); + } + } + } + + pDesc->pFuncName = new String( ScCompiler::GetNativeSymbol( static_cast<OpCode>( aRes.GetId()))); + pDesc->pFuncDesc = new String(ScResId(1)); + + if (nArgs) + { + pDesc->ppDefArgNames = new String*[nArgs]; + pDesc->ppDefArgDescs = new String*[nArgs]; + for (USHORT i = 0; i < nArgs; i++) + { + pDesc->ppDefArgNames[i] = new String(ScResId(2*(i+1) )); + pDesc->ppDefArgDescs[i] = new String(ScResId(2*(i+1)+1)); + } + } + + FreeResource(); +} + +//------------------------------------------------------------------------ + +USHORT ScFuncRes::GetNum() +{ + return ReadShortRes(); +} + +//========================================================================= + +// um an die protected von Resource ranzukommen +class ScResourcePublisher : public Resource +{ +private: + void FreeResource() { Resource::FreeResource(); } +public: + ScResourcePublisher( const ScResId& rId ) : Resource( rId ) {} + ~ScResourcePublisher() { FreeResource(); } + BOOL IsAvailableRes( const ResId& rId ) const + { return Resource::IsAvailableRes( rId ); } + +}; + + +ScFunctionList::ScFunctionList() : + nMaxFuncNameLen ( 0 ) +{ + ScFuncDesc* pDesc = NULL; + xub_StrLen nStrLen = 0; + FuncCollection* pFuncColl; + USHORT i,j; + USHORT nDescBlock[] = + { + RID_SC_FUNCTION_DESCRIPTIONS1, + RID_SC_FUNCTION_DESCRIPTIONS2 + }; + const USHORT nBlocks = sizeof(nDescBlock) / sizeof(USHORT); + + aFunctionList.Clear(); + + for ( USHORT k = 0; k < nBlocks; k++ ) + { + ::std::auto_ptr<ScResourcePublisher> pBlock( new ScResourcePublisher( ScResId( nDescBlock[k] ) ) ); + // Browse for all possible OpCodes. This is not the fastest method, but + // otherwise the sub resources within the resource blocks and the + // resource blocks themselfs would had to be ordered according to + // OpCodes, which is utopian.. + for (i = 0; i <= SC_OPCODE_LAST_OPCODE_ID; i++) + { + ScResId aRes(i); + aRes.SetRT(RSC_RESOURCE); + // Sub resource of OpCode available? + if (pBlock->IsAvailableRes(aRes)) + { + pDesc = new ScFuncDesc; + bool bSuppressed = false; + ScFuncRes aSubRes( aRes, pDesc, bSuppressed); + // Instead of dealing with this exceptional case at 1001 places + // we simply don't add an entirely suppressed function to the + // list and delete it. + if (bSuppressed) + delete pDesc; + else + { + pDesc->nFIndex = i; + aFunctionList.Insert( pDesc, LIST_APPEND ); + + nStrLen = (*(pDesc->pFuncName)).Len(); + if (nStrLen > nMaxFuncNameLen) + nMaxFuncNameLen = nStrLen; + } + } + } + } + + USHORT nNextId = SC_OPCODE_LAST_OPCODE_ID + 1; // FuncID for AddIn functions + + // Auswertung AddIn-Liste + String aDefArgNameValue(RTL_CONSTASCII_STRINGPARAM("value")); + String aDefArgNameString(RTL_CONSTASCII_STRINGPARAM("string")); + String aDefArgNameValues(RTL_CONSTASCII_STRINGPARAM("values")); + String aDefArgNameStrings(RTL_CONSTASCII_STRINGPARAM("strings")); + String aDefArgNameCells(RTL_CONSTASCII_STRINGPARAM("cells")); + String aDefArgNameNone(RTL_CONSTASCII_STRINGPARAM("none")); + String aDefArgDescValue(RTL_CONSTASCII_STRINGPARAM("a value")); + String aDefArgDescString(RTL_CONSTASCII_STRINGPARAM("a string")); + String aDefArgDescValues(RTL_CONSTASCII_STRINGPARAM("array of values")); + String aDefArgDescStrings(RTL_CONSTASCII_STRINGPARAM("array of strings")); + String aDefArgDescCells(RTL_CONSTASCII_STRINGPARAM("range of cells")); + String aDefArgDescNone(RTL_CONSTASCII_STRINGPARAM("none")); + String aArgName, aArgDesc; + pFuncColl = ScGlobal::GetFuncCollection(); + for (i = 0; i < pFuncColl->GetCount(); i++) + { + pDesc = new ScFuncDesc; + FuncData *pAddInFuncData = (FuncData*)pFuncColl->At(i); + USHORT nArgs = pAddInFuncData->GetParamCount() - 1; + pAddInFuncData->GetParamDesc( aArgName, aArgDesc, 0 ); + pDesc->nFIndex = nNextId++; // ??? OpCode vergeben + pDesc->nCategory = ID_FUNCTION_GRP_ADDINS; + pDesc->pFuncName = new String(pAddInFuncData->GetInternalName()); + pDesc->pFuncName->ToUpperAscii(); + pDesc->pFuncDesc = new String( aArgDesc ); + *(pDesc->pFuncDesc) += '\n'; + pDesc->pFuncDesc->AppendAscii(RTL_CONSTASCII_STRINGPARAM( "( AddIn: " )); + *(pDesc->pFuncDesc) += pAddInFuncData->GetModuleName(); + pDesc->pFuncDesc->AppendAscii(RTL_CONSTASCII_STRINGPARAM( " )" )); + pDesc->nArgCount = nArgs; + if (nArgs) + { + pDesc->pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgs]; + pDesc->ppDefArgNames = new String*[nArgs]; + pDesc->ppDefArgDescs = new String*[nArgs]; + for (j = 0; j < nArgs; j++) + { + pDesc->pDefArgFlags[j].bOptional = false; + pDesc->pDefArgFlags[j].bSuppress = false; + pAddInFuncData->GetParamDesc( aArgName, aArgDesc, j+1 ); + if ( aArgName.Len() ) + pDesc->ppDefArgNames[j] = new String( aArgName ); + else + { + switch (pAddInFuncData->GetParamType(j+1)) + { + case PTR_DOUBLE: + pDesc->ppDefArgNames[j] = new String( aDefArgNameValue ); + break; + case PTR_STRING: + pDesc->ppDefArgNames[j] = new String( aDefArgNameString ); + break; + case PTR_DOUBLE_ARR: + pDesc->ppDefArgNames[j] = new String( aDefArgNameValues ); + break; + case PTR_STRING_ARR: + pDesc->ppDefArgNames[j] = new String( aDefArgNameStrings ); + break; + case PTR_CELL_ARR: + pDesc->ppDefArgNames[j] = new String( aDefArgNameCells ); + break; + default: + pDesc->ppDefArgNames[j] = new String( aDefArgNameNone ); + break; + } + } + if ( aArgDesc.Len() ) + pDesc->ppDefArgDescs[j] = new String( aArgDesc ); + else + { + switch (pAddInFuncData->GetParamType(j+1)) + { + case PTR_DOUBLE: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescValue ); + break; + case PTR_STRING: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescString ); + break; + case PTR_DOUBLE_ARR: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescValues ); + break; + case PTR_STRING_ARR: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescStrings ); + break; + case PTR_CELL_ARR: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescCells ); + break; + default: + pDesc->ppDefArgDescs[j] = new String( aDefArgDescNone ); + break; + } + } + } + } +// pDesc->nHelpId = 0; + + aFunctionList.Insert(pDesc, LIST_APPEND); + nStrLen = (*(pDesc->pFuncName)).Len(); + if ( nStrLen > nMaxFuncNameLen) + nMaxFuncNameLen = nStrLen; + } + + // StarOne AddIns + + ScUnoAddInCollection* pUnoAddIns = ScGlobal::GetAddInCollection(); + long nUnoCount = pUnoAddIns->GetFuncCount(); + for (long nFunc=0; nFunc<nUnoCount; nFunc++) + { + pDesc = new ScFuncDesc; + pDesc->nFIndex = nNextId++; + + if ( pUnoAddIns->FillFunctionDesc( nFunc, *pDesc ) ) + { + aFunctionList.Insert(pDesc, LIST_APPEND); + nStrLen = (*(pDesc->pFuncName)).Len(); + if (nStrLen > nMaxFuncNameLen) + nMaxFuncNameLen = nStrLen; + } + else + delete pDesc; + } +} + +//------------------------------------------------------------------------ + +ScFunctionList::~ScFunctionList() +{ + const ScFuncDesc* pDesc = First(); + while (pDesc) + { + delete pDesc; + pDesc = Next(); + } +} + + +//======================================================================== +// class ScFuncDesc: + +ScFuncDesc::ScFuncDesc() : + pFuncName (NULL), + pFuncDesc (NULL), + ppDefArgNames (NULL), + ppDefArgDescs (NULL), + pDefArgFlags (NULL), + nFIndex (0), + nCategory (0), + nArgCount (0), + nHelpId (0), + bIncomplete (false), + bHasSuppressedArgs(false) +{} + +//------------------------------------------------------------------------ + +ScFuncDesc::~ScFuncDesc() +{ + Clear(); +} + +//------------------------------------------------------------------------ + +void ScFuncDesc::Clear() +{ + USHORT nArgs = nArgCount; + if (nArgs >= VAR_ARGS) nArgs -= VAR_ARGS-1; + if (nArgs) + { + for (USHORT i=0; i<nArgs; i++ ) + { + delete ppDefArgNames[i]; + delete ppDefArgDescs[i]; + } + delete [] ppDefArgNames; + delete [] ppDefArgDescs; + delete [] pDefArgFlags; + } + nArgCount = 0; + ppDefArgNames = NULL; + ppDefArgDescs = NULL; + pDefArgFlags = NULL; + + delete pFuncName; + pFuncName = NULL; + + delete pFuncDesc; + pFuncDesc = NULL; + + nFIndex = 0; + nCategory = 0; + nHelpId = 0; + bIncomplete = false; + bHasSuppressedArgs = false; +} + +//------------------------------------------------------------------------ + +String ScFuncDesc::GetParamList() const +{ + const String& sep = ScCompiler::GetNativeSymbol(ocSep); + + String aSig; + + if ( nArgCount > 0 ) + { + if ( nArgCount < VAR_ARGS ) + { + USHORT nLastSuppressed = nArgCount; + USHORT nLastAdded = nArgCount; + for ( USHORT i=0; i<nArgCount; i++ ) + { + if (pDefArgFlags[i].bSuppress) + nLastSuppressed = i; + else + { + nLastAdded = i; + aSig += *(ppDefArgNames[i]); + if ( i != nArgCount-1 ) + { + aSig.Append(sep); + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " " )); + } + } + } + // If only suppressed parameters follow the last added parameter, + // remove one "; " + if (nLastSuppressed < nArgCount && nLastAdded < nLastSuppressed && + aSig.Len() >= 2) + aSig.Erase( aSig.Len() - 2 ); + } + else + { + USHORT nFix = nArgCount - VAR_ARGS; + for ( USHORT nArg = 0; nArg < nFix; nArg++ ) + { + if (!pDefArgFlags[nArg].bSuppress) + { + aSig += *(ppDefArgNames[nArg]); + aSig.Append(sep); + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " " )); + } + } + /* NOTE: Currently there are no suppressed var args parameters. If + * there were, we'd have to cope with it here and above for the fix + * parameters. For now parameters are always added, so no special + * treatment of a trailing "; " necessary. */ + aSig += *(ppDefArgNames[nFix]); + aSig += '1'; + aSig.Append(sep); + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " " )); + aSig += *(ppDefArgNames[nFix]); + aSig += '2'; + aSig.Append(sep); + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( " ... " )); + } + } + + return aSig; +} + +//------------------------------------------------------------------------ + +String ScFuncDesc::GetSignature() const +{ + String aSig; + + if(pFuncName) + { + aSig = *pFuncName; + + String aParamList( GetParamList() ); + if( aParamList.Len() ) + { + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( "( " )); + aSig.Append( aParamList ); + // U+00A0 (NBSP) prevents automatic line break + aSig.Append( static_cast< sal_Unicode >(0xA0) ).Append( ')' ); + } + else + aSig.AppendAscii(RTL_CONSTASCII_STRINGPARAM( "()" )); + } + return aSig; +} + +//------------------------------------------------------------------------ + +::rtl::OUString ScFuncDesc::getFormula( const ::std::vector< ::rtl::OUString >& _aArguments ) const +{ + const String& sep = ScCompiler::GetNativeSymbol(ocSep); + + ::rtl::OUStringBuffer aFormula; + + if(pFuncName) + { + aFormula.append( *pFuncName ); + + aFormula.appendAscii( "(" ); + ::std::vector< ::rtl::OUString >::const_iterator aIter = _aArguments.begin(); + ::std::vector< ::rtl::OUString >::const_iterator aEnd = _aArguments.end(); + + if ( nArgCount > 0 && aIter != aEnd ) + { + BOOL bLastArg = ( aIter->getLength() == 0 ); + + while( aIter != aEnd && !bLastArg ) + { + aFormula.append( *(aIter) ); + if ( aIter != (aEnd-1) ) + { + bLastArg = !( (aIter+1)->getLength() > 0 ); + if ( !bLastArg ) + aFormula.append( sep ); + } + + ++aIter; + } + } + + aFormula.appendAscii( ")" ); + } + return aFormula.makeStringAndClear(); +} + +//------------------------------------------------------------------------ + +USHORT ScFuncDesc::GetSuppressedArgCount() const +{ + if (!bHasSuppressedArgs || !pDefArgFlags) + return nArgCount; + + USHORT nArgs = nArgCount; + if (nArgs >= VAR_ARGS) + nArgs -= VAR_ARGS - 1; + USHORT nCount = nArgs; + for (USHORT i=0; i < nArgs; ++i) + { + if (pDefArgFlags[i].bSuppress) + --nCount; + } + if (nArgCount >= VAR_ARGS) + nCount += VAR_ARGS - 1; + return nCount; +} + +//------------------------------------------------------------------------ + +::rtl::OUString ScFuncDesc::getFunctionName() const +{ + ::rtl::OUString sRet; + if ( pFuncName ) + sRet = *pFuncName; + return sRet; +} +// ----------------------------------------------------------------------------- +const formula::IFunctionCategory* ScFuncDesc::getCategory() const +{ + return ScGlobal::GetStarCalcFunctionMgr()->getCategory(nCategory); +} +// ----------------------------------------------------------------------------- +::rtl::OUString ScFuncDesc::getDescription() const +{ + ::rtl::OUString sRet; + if ( pFuncDesc ) + sRet = *pFuncDesc; + return sRet; +} +// ----------------------------------------------------------------------------- +// GetSuppressedArgCount +xub_StrLen ScFuncDesc::getSuppressedArgumentCount() const +{ + return GetSuppressedArgCount(); +} +// ----------------------------------------------------------------------------- +// +void ScFuncDesc::fillVisibleArgumentMapping(::std::vector<USHORT>& _rArguments) const +{ + if (!bHasSuppressedArgs || !pDefArgFlags) + { + _rArguments.resize( nArgCount); + ::std::iota( _rArguments.begin(), _rArguments.end(), 0); + } + + _rArguments.reserve( nArgCount); + USHORT nArgs = nArgCount; + if (nArgs >= VAR_ARGS) + nArgs -= VAR_ARGS - 1; + for (USHORT i=0; i < nArgs; ++i) + { + if (!pDefArgFlags[i].bSuppress) + _rArguments.push_back(i); + } +} +// ----------------------------------------------------------------------------- +void ScFuncDesc::initArgumentInfo() const +{ + // get the full argument description + // (add-in has to be instantiated to get the type information) + + if ( bIncomplete && pFuncName ) + { + ScUnoAddInCollection& rAddIns = *ScGlobal::GetAddInCollection(); + String aIntName = rAddIns.FindFunction( *pFuncName, TRUE ); // pFuncName is upper-case + + if ( aIntName.Len() ) + { + // GetFuncData with bComplete=true loads the component and updates + // the global function list if needed. + + rAddIns.GetFuncData( aIntName, true ); + } + + if ( bIncomplete ) + { + DBG_ERRORFILE( "couldn't initialize add-in function" ); + const_cast<ScFuncDesc*>(this)->bIncomplete = FALSE; // even if there was an error, don't try again + } + } +} +// ----------------------------------------------------------------------------- +::rtl::OUString ScFuncDesc::getSignature() const +{ + return GetSignature(); +} +// ----------------------------------------------------------------------------- +long ScFuncDesc::getHelpId() const +{ + return nHelpId; +} +// ----------------------------------------------------------------------------- + +// parameter +sal_uInt32 ScFuncDesc::getParameterCount() const +{ + return nArgCount; +} +// ----------------------------------------------------------------------------- +::rtl::OUString ScFuncDesc::getParameterName(sal_uInt32 _nPos) const +{ + return *(ppDefArgNames[_nPos]); +} +// ----------------------------------------------------------------------------- +::rtl::OUString ScFuncDesc::getParameterDescription(sal_uInt32 _nPos) const +{ + return *(ppDefArgDescs[_nPos]); +} +// ----------------------------------------------------------------------------- +bool ScFuncDesc::isParameterOptional(sal_uInt32 _nPos) const +{ + return pDefArgFlags[_nPos].bOptional; +} +// ----------------------------------------------------------------------------- +//======================================================================== +// class ScFunctionMgr: + +ScFunctionMgr::ScFunctionMgr() + : pFuncList ( ScGlobal::GetStarCalcFunctionList() ), + pCurCatList ( NULL ) +{ + DBG_ASSERT( pFuncList, "Funktionsliste nicht gefunden." ); + ULONG nCount = pFuncList->GetCount(); + const ScFuncDesc* pDesc; + List* pRootList; + ULONG n; + + for ( USHORT i=0; i<MAX_FUNCCAT; i++ ) // Kategorie-Listen erstellen + aCatLists[i] = new List; + + pRootList = aCatLists[0]; // Gesamtliste ("Alle") erstellen + CollatorWrapper* pCaseCollator = ScGlobal::GetCaseCollator(); + for ( n=0; n<nCount; n++ ) + { + ULONG nTmpCnt=0; + pDesc = pFuncList->GetFunction(n); + for (nTmpCnt = 0; nTmpCnt < n; nTmpCnt++) + { + // ist zwar case-sensitiv, aber Umlaute muessen richtig einsortiert werden + + const ScFuncDesc* pTmpDesc = (const ScFuncDesc*)pRootList->GetObject(nTmpCnt); + if ( pCaseCollator->compareString(*pDesc->pFuncName, *pTmpDesc->pFuncName ) == COMPARE_LESS ) + break; + } + pRootList->Insert((void*)pDesc, nTmpCnt); // Einsortieren + } + + for ( n=0; n<nCount; n++ ) // in Gruppenlisten kopieren + { + pDesc = (const ScFuncDesc*)pRootList->GetObject(n); + DBG_ASSERT((pDesc->nCategory) < MAX_FUNCCAT, "Unbekannte Kategorie"); + if ((pDesc->nCategory) < MAX_FUNCCAT) + aCatLists[pDesc->nCategory]->Insert((void*)pDesc, LIST_APPEND); + } +} + +//------------------------------------------------------------------------ + +ScFunctionMgr::~ScFunctionMgr() +{ + for (USHORT i = 0; i < MAX_FUNCCAT; i++) + delete aCatLists[i]; +// delete pFuncList; // Macht spaeter die App +} + +//------------------------------------------------------------------------ + +const ScFuncDesc* ScFunctionMgr::Get( const String& rFName ) const +{ + const ScFuncDesc* pDesc = NULL; + if (rFName.Len() <= pFuncList->GetMaxFuncNameLen()) + for (pDesc = First(0); pDesc; pDesc = Next()) + if (rFName.EqualsIgnoreCaseAscii(*(pDesc->pFuncName))) + break; + return pDesc; +} + +//------------------------------------------------------------------------ + +const ScFuncDesc* ScFunctionMgr::Get( USHORT nFIndex ) const +{ + const ScFuncDesc* pDesc; + for (pDesc = First(0); pDesc; pDesc = Next()) + if (pDesc->nFIndex == nFIndex) + break; + return pDesc; +} + +//------------------------------------------------------------------------ + +const ScFuncDesc* ScFunctionMgr::First( USHORT nCategory ) const +{ + DBG_ASSERT( nCategory < MAX_FUNCCAT, "Unbekannte Kategorie" ); + + if ( nCategory < MAX_FUNCCAT ) + { + pCurCatList = aCatLists[nCategory]; + return (const ScFuncDesc*)pCurCatList->First(); + } + else + { + pCurCatList = NULL; + return NULL; + } +} + +//------------------------------------------------------------------------ + +const ScFuncDesc* ScFunctionMgr::Next() const +{ + if ( pCurCatList ) + return (const ScFuncDesc*)pCurCatList->Next(); + else + return NULL; +} +sal_uInt32 ScFunctionMgr::getCount() const +{ + return MAX_FUNCCAT - 1; +} +const formula::IFunctionCategory* ScFunctionMgr::getCategory(sal_uInt32 nCategory) const +{ + formula::IFunctionCategory* pRet = NULL; + if ( nCategory < (MAX_FUNCCAT-1) ) + { + pRet = new ScFunctionCategory(const_cast<ScFunctionMgr*>(this),aCatLists[nCategory+1],nCategory); // aCatLists[0] is "all" + } + return pRet; +} +// ----------------------------------------------------------------------------- +const formula::IFunctionDescription* ScFunctionMgr::getFunctionByName(const ::rtl::OUString& _sFunctionName) const +{ + return Get(_sFunctionName); +} +// ----------------------------------------------------------------------------- +void ScFunctionMgr::fillLastRecentlyUsedFunctions(::std::vector< const formula::IFunctionDescription*>& _rLastRUFunctions) const +{ +#define LRU_MAX 10 + + const ScAppOptions& rAppOpt = SC_MOD()->GetAppOptions(); + USHORT nLRUFuncCount = Min( rAppOpt.GetLRUFuncListCount(), (USHORT)LRU_MAX ); + USHORT* pLRUListIds = rAppOpt.GetLRUFuncList(); + + if ( pLRUListIds ) + { + for ( USHORT i=0; i<nLRUFuncCount; i++ ) + _rLastRUFunctions.push_back( Get( pLRUListIds[i] ) ); + } +} +// ----------------------------------------------------------------------------- +String ScFunctionMgr::GetCategoryName(sal_uInt32 _nCategoryNumber ) +{ + if ( _nCategoryNumber > SC_FUNCGROUP_COUNT ) + { + DBG_ERROR("Invalid category number!"); + return String(); + } // if ( _nCategoryNumber >= SC_FUNCGROUP_COUNT ) + + ::std::auto_ptr<ScResourcePublisher> pCategories( new ScResourcePublisher( ScResId( RID_FUNCTION_CATEGORIES ) ) ); + return String(ScResId((USHORT)_nCategoryNumber)); +} +sal_Unicode ScFunctionMgr::getSingleToken(const formula::IFunctionManager::EToken _eToken) const +{ + switch(_eToken) + { + case eOk: + return ScCompiler::GetNativeSymbol(ocOpen).GetChar(0); + case eClose: + return ScCompiler::GetNativeSymbol(ocClose).GetChar(0); + case eSep: + return ScCompiler::GetNativeSymbol(ocSep).GetChar(0); + case eArrayOpen: + return ScCompiler::GetNativeSymbol(ocArrayOpen).GetChar(0); + case eArrayClose: + return ScCompiler::GetNativeSymbol(ocArrayClose).GetChar(0); + } // switch(_eToken) + return 0; +} +// ----------------------------------------------------------------------------- +sal_uInt32 ScFunctionCategory::getCount() const +{ + return m_pCategory->Count(); +} +// ----------------------------------------------------------------------------- +const formula::IFunctionManager* ScFunctionCategory::getFunctionManager() const +{ + return m_pMgr; +} +// ----------------------------------------------------------------------------- +::rtl::OUString ScFunctionCategory::getName() const +{ + if ( !m_sName.getLength() ) + m_sName = ScFunctionMgr::GetCategoryName(m_nCategory+1); + return m_sName; +} +// ----------------------------------------------------------------------------- +const formula::IFunctionDescription* ScFunctionCategory::getFunction(sal_uInt32 _nPos) const +{ + const ScFuncDesc* pDesc = NULL; + sal_uInt32 i = 0; + for (pDesc = (const ScFuncDesc*)m_pCategory->First(); i < _nPos && pDesc; pDesc = (const ScFuncDesc*)m_pCategory->Next(),++i) + ; + return pDesc; +} +// ----------------------------------------------------------------------------- +sal_uInt32 ScFunctionCategory::getNumber() const +{ + return m_nCategory; +} +// ----------------------------------------------------------------------------- + +//------------------------------------------------------------------------ + +utl::TransliterationWrapper* ScGlobal::GetpTransliteration() //add by CHINA001 +{ + if ( !pTransliteration ) + { + const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguage(); + pTransliteration = new ::utl::TransliterationWrapper( + ::comphelper::getProcessServiceFactory(), SC_TRANSLITERATION_IGNORECASE ); + pTransliteration->loadModuleIfNeeded( eOfficeLanguage ); + } + DBG_ASSERT( + pTransliteration, + "ScGlobal::GetpTransliteration() called before ScGlobal::Init()"); + return pTransliteration; +} + +const LocaleDataWrapper* ScGlobal::GetpLocaleData() +{ + DBG_ASSERT( + pLocaleData, + "ScGlobal::GetpLocaleData() called before ScGlobal::Init()"); + return pLocaleData; +} +CalendarWrapper* ScGlobal::GetCalendar() +{ + if ( !pCalendar ) + { + pCalendar = new CalendarWrapper( ::comphelper::getProcessServiceFactory() ); + pCalendar->loadDefaultCalendar( *GetLocale() ); + } + return pCalendar; +} +CollatorWrapper* ScGlobal::GetCollator() +{ + if ( !pCollator ) + { + pCollator = new CollatorWrapper( ::comphelper::getProcessServiceFactory() ); + pCollator->loadDefaultCollator( *GetLocale(), SC_COLLATOR_IGNORES ); + } // if ( !pCollator ) + return pCollator; +} +CollatorWrapper* ScGlobal::GetCaseCollator() +{ + if ( !pCaseCollator ) + { + pCaseCollator = new CollatorWrapper( ::comphelper::getProcessServiceFactory() ); + pCaseCollator->loadDefaultCollator( *GetLocale(), 0 ); + } // if ( !pCaseCollator ) + return pCaseCollator; +} +::utl::TransliterationWrapper* ScGlobal::GetCaseTransliteration() +{ + if ( !pCaseTransliteration ) + { + const LanguageType eOfficeLanguage = Application::GetSettings().GetLanguage(); + pCaseTransliteration = new ::utl::TransliterationWrapper(::comphelper::getProcessServiceFactory(), SC_TRANSLITERATION_CASESENSE ); + pCaseTransliteration->loadModuleIfNeeded( eOfficeLanguage ); + } // if ( !pCaseTransliteration ) + return pCaseTransliteration; +} +IntlWrapper* ScGlobal::GetScIntlWrapper() +{ + if ( !pScIntlWrapper ) + { + pScIntlWrapper = new IntlWrapper( ::comphelper::getProcessServiceFactory(), *GetLocale() ); + } + return pScIntlWrapper; +} +::com::sun::star::lang::Locale* ScGlobal::GetLocale() +{ + if ( !pLocale ) + { + pLocale = new ::com::sun::star::lang::Locale( Application::GetSettings().GetLocale()); + } + return pLocale; +} + diff --git a/sc/source/core/data/global2.cxx b/sc/source/core/data/global2.cxx new file mode 100644 index 000000000000..6af662119f12 --- /dev/null +++ b/sc/source/core/data/global2.cxx @@ -0,0 +1,1250 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: global2.cxx,v $ + * $Revision: 1.23.32.2 $ + * + * 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 <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <unotools/textsearch.hxx> +#include <svtools/pathoptions.hxx> +#include <svtools/useroptions.hxx> +#include <tools/urlobj.hxx> +#include <unotools/charclass.hxx> +#include <stdlib.h> +#include <ctype.h> +#include <svtools/syslocale.hxx> + +#include "global.hxx" +#include "rangeutl.hxx" +#include "pivot.hxx" +#include "rechead.hxx" +#include "compiler.hxx" +#include "paramisc.hxx" + +#include "sc.hrc" +#include "globstr.hrc" + + +// ----------------------------------------------------------------------- + + + +#define MAX_LABELS 256 //!!! aus fieldwnd.hxx, muss noch nach global.hxx ??? + +//------------------------------------------------------------------------ +// struct ScImportParam: + +ScImportParam::ScImportParam() : + nCol1(0), + nRow1(0), + nCol2(0), + nRow2(0), + bImport(FALSE), + bNative(FALSE), + bSql(TRUE), + nType(ScDbTable) +{ +} + +ScImportParam::ScImportParam( const ScImportParam& r ) : + nCol1 (r.nCol1), + nRow1 (r.nRow1), + nCol2 (r.nCol2), + nRow2 (r.nRow2), + bImport (r.bImport), + aDBName (r.aDBName), + aStatement (r.aStatement), + bNative (r.bNative), + bSql (r.bSql), + nType (r.nType) +{ +} + +ScImportParam::~ScImportParam() +{ +} + +//UNUSED2009-05 void ScImportParam::Clear() +//UNUSED2009-05 { +//UNUSED2009-05 nCol1 = nCol2 = 0; +//UNUSED2009-05 nRow1 = nRow2 = 0; +//UNUSED2009-05 bImport = FALSE; +//UNUSED2009-05 bNative = FALSE; +//UNUSED2009-05 bSql = TRUE; +//UNUSED2009-05 nType = ScDbTable; +//UNUSED2009-05 aDBName.Erase(); +//UNUSED2009-05 aStatement.Erase(); +//UNUSED2009-05 } + +ScImportParam& ScImportParam::operator=( const ScImportParam& r ) +{ + nCol1 = r.nCol1; + nRow1 = r.nRow1; + nCol2 = r.nCol2; + nRow2 = r.nRow2; + bImport = r.bImport; + aDBName = r.aDBName; + aStatement = r.aStatement; + bNative = r.bNative; + bSql = r.bSql; + nType = r.nType; + + return *this; +} + +BOOL ScImportParam::operator==( const ScImportParam& rOther ) const +{ + return( nCol1 == rOther.nCol1 && + nRow1 == rOther.nRow1 && + nCol2 == rOther.nCol2 && + nRow2 == rOther.nRow2 && + bImport == rOther.bImport && + aDBName == rOther.aDBName && + aStatement == rOther.aStatement && + bNative == rOther.bNative && + bSql == rOther.bSql && + nType == rOther.nType ); + + //! nQuerySh und pConnection sind gleich ? +} + + +//------------------------------------------------------------------------ +// struct ScQueryParam: + +ScQueryEntry::ScQueryEntry() +{ + bDoQuery = FALSE; + bQueryByString = FALSE; + eOp = SC_EQUAL; + eConnect = SC_AND; + nField = 0; + nVal = 0.0; + pStr = new String; + pSearchParam = NULL; + pSearchText = NULL; +} + +ScQueryEntry::ScQueryEntry(const ScQueryEntry& r) +{ + bDoQuery = r.bDoQuery; + bQueryByString = r.bQueryByString; + eOp = r.eOp; + eConnect = r.eConnect; + nField = r.nField; + nVal = r.nVal; + pStr = new String(*r.pStr); + pSearchParam = NULL; + pSearchText = NULL; +} + +ScQueryEntry::~ScQueryEntry() +{ + delete pStr; + if ( pSearchParam ) + { + delete pSearchParam; + delete pSearchText; + } +} + +ScQueryEntry& ScQueryEntry::operator=( const ScQueryEntry& r ) +{ + bDoQuery = r.bDoQuery; + bQueryByString = r.bQueryByString; + eOp = r.eOp; + eConnect = r.eConnect; + nField = r.nField; + nVal = r.nVal; + *pStr = *r.pStr; + if ( pSearchParam ) + { + delete pSearchParam; + delete pSearchText; + } + pSearchParam = NULL; + pSearchText = NULL; + + return *this; +} + +void ScQueryEntry::Clear() +{ + bDoQuery = FALSE; + bQueryByString = FALSE; + eOp = SC_EQUAL; + eConnect = SC_AND; + nField = 0; + nVal = 0.0; + pStr->Erase(); + if ( pSearchParam ) + { + delete pSearchParam; + delete pSearchText; + } + pSearchParam = NULL; + pSearchText = NULL; +} + +BOOL ScQueryEntry::operator==( const ScQueryEntry& r ) const +{ + return bDoQuery == r.bDoQuery + && bQueryByString == r.bQueryByString + && eOp == r.eOp + && eConnect == r.eConnect + && nField == r.nField + && nVal == r.nVal + && *pStr == *r.pStr; + //! pSearchParam und pSearchText nicht vergleichen +} + +utl::TextSearch* ScQueryEntry::GetSearchTextPtr( BOOL bCaseSens ) +{ + if ( !pSearchParam ) + { + pSearchParam = new utl::SearchParam( *pStr, utl::SearchParam::SRCH_REGEXP, + bCaseSens, FALSE, FALSE ); + pSearchText = new utl::TextSearch( *pSearchParam, *ScGlobal::pCharClass ); + } + return pSearchText; +} + +//------------------------------------------------------------------------ + +ScQueryParam::ScQueryParam() +{ + nEntryCount = 0; + Clear(); +} + +//------------------------------------------------------------------------ + +ScQueryParam::ScQueryParam( const ScQueryParam& r ) : + nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2),nTab(r.nTab), + bHasHeader(r.bHasHeader), bByRow(r.bByRow), bInplace(r.bInplace), bCaseSens(r.bCaseSens), + bRegExp(r.bRegExp), bMixedComparison(r.bMixedComparison), + bDuplicate(r.bDuplicate), bDestPers(r.bDestPers), + nDestTab(r.nDestTab), nDestCol(r.nDestCol), nDestRow(r.nDestRow) +{ + nEntryCount = 0; + + Resize( r.nEntryCount ); + for (USHORT i=0; i<nEntryCount; i++) + pEntries[i] = r.pEntries[i]; +} + +//------------------------------------------------------------------------ + +ScQueryParam::~ScQueryParam() +{ + delete[] pEntries; +} + +//------------------------------------------------------------------------ + +void ScQueryParam::Clear() +{ + nCol1=nCol2=nDestCol = 0; + nRow1=nRow2=nDestRow = 0; + nDestTab = 0; + nTab = SCTAB_MAX; + bHasHeader = bCaseSens = bRegExp = bMixedComparison = FALSE; + bInplace = bByRow = bDuplicate = bDestPers = TRUE; + + Resize( MAXQUERY ); + for (USHORT i=0; i<MAXQUERY; i++) + pEntries[i].Clear(); +} + +//------------------------------------------------------------------------ + +ScQueryParam& ScQueryParam::operator=( const ScQueryParam& r ) +{ + nCol1 = r.nCol1; + nRow1 = r.nRow1; + nCol2 = r.nCol2; + nRow2 = r.nRow2; + nTab = r.nTab; + nDestTab = r.nDestTab; + nDestCol = r.nDestCol; + nDestRow = r.nDestRow; + bHasHeader = r.bHasHeader; + bInplace = r.bInplace; + bCaseSens = r.bCaseSens; + bRegExp = r.bRegExp; + bMixedComparison = r.bMixedComparison; + bDuplicate = r.bDuplicate; + bByRow = r.bByRow; + bDestPers = r.bDestPers; + + Resize( r.nEntryCount ); + for (USHORT i=0; i<nEntryCount; i++) + pEntries[i] = r.pEntries[i]; + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScQueryParam::operator==( const ScQueryParam& rOther ) const +{ + BOOL bEqual = FALSE; + + // Anzahl der Queries gleich? + USHORT nUsed = 0; + USHORT nOtherUsed = 0; + while ( nUsed<nEntryCount && pEntries[nUsed].bDoQuery ) ++nUsed; + while ( nOtherUsed<rOther.nEntryCount && rOther.pEntries[nOtherUsed].bDoQuery ) + ++nOtherUsed; + + if ( (nUsed == nOtherUsed) + && (nCol1 == rOther.nCol1) + && (nRow1 == rOther.nRow1) + && (nCol2 == rOther.nCol2) + && (nRow2 == rOther.nRow2) + && (nTab == rOther.nTab) + && (bHasHeader == rOther.bHasHeader) + && (bByRow == rOther.bByRow) + && (bInplace == rOther.bInplace) + && (bCaseSens == rOther.bCaseSens) + && (bRegExp == rOther.bRegExp) + && (bMixedComparison == rOther.bMixedComparison) + && (bDuplicate == rOther.bDuplicate) + && (bDestPers == rOther.bDestPers) + && (nDestTab == rOther.nDestTab) + && (nDestCol == rOther.nDestCol) + && (nDestRow == rOther.nDestRow) ) + { + bEqual = TRUE; + for ( USHORT i=0; i<nUsed && bEqual; i++ ) + bEqual = pEntries[i] == rOther.pEntries[i]; + } + return bEqual; +} + +//------------------------------------------------------------------------ + +void ScQueryParam::DeleteQuery( SCSIZE nPos ) +{ + if (nPos<nEntryCount) + { + for (SCSIZE i=nPos; i+1<nEntryCount; i++) + pEntries[i] = pEntries[i+1]; + + pEntries[nEntryCount-1].Clear(); + } + else + { + DBG_ERROR("Falscher Parameter bei ScQueryParam::DeleteQuery"); + } +} + +//------------------------------------------------------------------------ + +void ScQueryParam::Resize(SCSIZE nNew) +{ + if ( nNew < MAXQUERY ) + nNew = MAXQUERY; // nie weniger als MAXQUERY + + ScQueryEntry* pNewEntries = NULL; + if ( nNew ) + pNewEntries = new ScQueryEntry[nNew]; + + SCSIZE nCopy = Min( nEntryCount, nNew ); + for (SCSIZE i=0; i<nCopy; i++) + pNewEntries[i] = pEntries[i]; + + if ( nEntryCount ) + delete[] pEntries; + nEntryCount = nNew; + pEntries = pNewEntries; +} + +//------------------------------------------------------------------------ + +void ScQueryParam::MoveToDest() +{ + if (!bInplace) + { + SCsCOL nDifX = ((SCsCOL) nDestCol) - ((SCsCOL) nCol1); + SCsROW nDifY = ((SCsROW) nDestRow) - ((SCsROW) nRow1); + SCsTAB nDifZ = ((SCsTAB) nDestTab) - ((SCsTAB) nTab); + + nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nDifX ); + nRow1 = sal::static_int_cast<SCROW>( nRow1 + nDifY ); + nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nDifX ); + nRow2 = sal::static_int_cast<SCROW>( nRow2 + nDifY ); + nTab = sal::static_int_cast<SCTAB>( nTab + nDifZ ); + for (USHORT i=0; i<nEntryCount; i++) + pEntries[i].nField += nDifX; + + bInplace = TRUE; + } + else + { + DBG_ERROR("MoveToDest, bInplace == TRUE"); + } +} + +//------------------------------------------------------------------------ + +void ScQueryParam::FillInExcelSyntax(String& aCellStr, SCSIZE nIndex) +{ + if (aCellStr.Len() > 0) + { + if ( nIndex >= nEntryCount ) + Resize( nIndex+1 ); + + ScQueryEntry& rEntry = pEntries[nIndex]; + + rEntry.bDoQuery = TRUE; + // Operatoren herausfiltern + if (aCellStr.GetChar(0) == '<') + { + if (aCellStr.GetChar(1) == '>') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_NOT_EQUAL; + } + else if (aCellStr.GetChar(1) == '=') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_LESS_EQUAL; + } + else + { + *rEntry.pStr = aCellStr.Copy(1); + rEntry.eOp = SC_LESS; + } + } + else if (aCellStr.GetChar(0) == '>') + { + if (aCellStr.GetChar(1) == '=') + { + *rEntry.pStr = aCellStr.Copy(2); + rEntry.eOp = SC_GREATER_EQUAL; + } + else + { + *rEntry.pStr = aCellStr.Copy(1); + rEntry.eOp = SC_GREATER; + } + } + else + { + if (aCellStr.GetChar(0) == '=') + *rEntry.pStr = aCellStr.Copy(1); + else + *rEntry.pStr = aCellStr; + rEntry.eOp = SC_EQUAL; + } + } +} + +//------------------------------------------------------------------------ +// struct ScSubTotalParam: + +ScSubTotalParam::ScSubTotalParam() +{ + for ( USHORT i=0; i<MAXSUBTOTAL; i++ ) + { + nSubTotals[i] = 0; + pSubTotals[i] = NULL; + pFunctions[i] = NULL; + } + + Clear(); +} + +//------------------------------------------------------------------------ + +ScSubTotalParam::ScSubTotalParam( const ScSubTotalParam& r ) : + nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2), + bRemoveOnly(r.bRemoveOnly),bReplace(r.bReplace),bPagebreak(r.bPagebreak),bCaseSens(r.bCaseSens), + bDoSort(r.bDoSort),bAscending(r.bAscending),bUserDef(r.bUserDef),nUserIndex(r.nUserIndex), + bIncludePattern(r.bIncludePattern) +{ + for (USHORT i=0; i<MAXSUBTOTAL; i++) + { + bGroupActive[i] = r.bGroupActive[i]; + nField[i] = r.nField[i]; + + if ( (r.nSubTotals[i] > 0) && r.pSubTotals[i] && r.pFunctions[i] ) + { + nSubTotals[i] = r.nSubTotals[i]; + pSubTotals[i] = new SCCOL [r.nSubTotals[i]]; + pFunctions[i] = new ScSubTotalFunc [r.nSubTotals[i]]; + + for (SCCOL j=0; j<r.nSubTotals[i]; j++) + { + pSubTotals[i][j] = r.pSubTotals[i][j]; + pFunctions[i][j] = r.pFunctions[i][j]; + } + } + else + { + nSubTotals[i] = 0; + pSubTotals[i] = NULL; + pFunctions[i] = NULL; + } + } +} + +//------------------------------------------------------------------------ + +void ScSubTotalParam::Clear() +{ + nCol1=nCol2= 0; + nRow1=nRow2 = 0; + nUserIndex = 0; + bPagebreak=bCaseSens=bUserDef=bIncludePattern=bRemoveOnly = FALSE; + bAscending=bReplace=bDoSort = TRUE; + + for (USHORT i=0; i<MAXSUBTOTAL; i++) + { + bGroupActive[i] = FALSE; + nField[i] = 0; + + if ( (nSubTotals[i] > 0) && pSubTotals[i] && pFunctions[i] ) + { + for ( SCCOL j=0; j<nSubTotals[i]; j++ ) { + pSubTotals[i][j] = 0; + pFunctions[i][j] = SUBTOTAL_FUNC_NONE; + } + } + } +} + +//------------------------------------------------------------------------ + +ScSubTotalParam& ScSubTotalParam::operator=( const ScSubTotalParam& r ) +{ + nCol1 = r.nCol1; + nRow1 = r.nRow1; + nCol2 = r.nCol2; + nRow2 = r.nRow2; + bRemoveOnly = r.bRemoveOnly; + bReplace = r.bReplace; + bPagebreak = r.bPagebreak; + bCaseSens = r.bCaseSens; + bDoSort = r.bDoSort; + bAscending = r.bAscending; + bUserDef = r.bUserDef; + nUserIndex = r.nUserIndex; + bIncludePattern = r.bIncludePattern; + + for (USHORT i=0; i<MAXSUBTOTAL; i++) + { + bGroupActive[i] = r.bGroupActive[i]; + nField[i] = r.nField[i]; + nSubTotals[i] = r.nSubTotals[i]; + + if ( pSubTotals[i] ) delete [] pSubTotals[i]; + if ( pFunctions[i] ) delete [] pFunctions[i]; + + if ( r.nSubTotals[i] > 0 ) + { + pSubTotals[i] = new SCCOL [r.nSubTotals[i]]; + pFunctions[i] = new ScSubTotalFunc [r.nSubTotals[i]]; + + for (SCCOL j=0; j<r.nSubTotals[i]; j++) + { + pSubTotals[i][j] = r.pSubTotals[i][j]; + pFunctions[i][j] = r.pFunctions[i][j]; + } + } + else + { + nSubTotals[i] = 0; + pSubTotals[i] = NULL; + pFunctions[i] = NULL; + } + } + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScSubTotalParam::operator==( const ScSubTotalParam& rOther ) const +{ + BOOL bEqual = (nCol1 == rOther.nCol1) + && (nRow1 == rOther.nRow1) + && (nCol2 == rOther.nCol2) + && (nRow2 == rOther.nRow2) + && (bRemoveOnly == rOther.bRemoveOnly) + && (bReplace == rOther.bReplace) + && (bPagebreak == rOther.bPagebreak) + && (bDoSort == rOther.bDoSort) + && (bCaseSens == rOther.bCaseSens) + && (bAscending == rOther.bAscending) + && (bUserDef == rOther.bUserDef) + && (nUserIndex == rOther.nUserIndex) + && (bIncludePattern== rOther.bIncludePattern); + + if ( bEqual ) + { + bEqual = TRUE; + for ( USHORT i=0; i<MAXSUBTOTAL && bEqual; i++ ) + { + bEqual = (bGroupActive[i] == rOther.bGroupActive[i]) + && (nField[i] == rOther.nField[i]) + && (nSubTotals[i] == rOther.nSubTotals[i]); + + if ( bEqual && (nSubTotals[i] > 0) ) + { + bEqual = (pSubTotals != NULL) && (pFunctions != NULL); + + for (SCCOL j=0; (j<nSubTotals[i]) && bEqual; j++) + { + bEqual = bEqual + && (pSubTotals[i][j] == rOther.pSubTotals[i][j]) + && (pFunctions[i][j] == rOther.pFunctions[i][j]); + } + } + } + } + + return bEqual; +} + +//------------------------------------------------------------------------ + +void ScSubTotalParam::SetSubTotals( USHORT nGroup, + const SCCOL* ptrSubTotals, + const ScSubTotalFunc* ptrFunctions, + USHORT nCount ) +{ + DBG_ASSERT( (nGroup <= MAXSUBTOTAL), + "ScSubTotalParam::SetSubTotals(): nGroup > MAXSUBTOTAL!" ); + DBG_ASSERT( ptrSubTotals, + "ScSubTotalParam::SetSubTotals(): ptrSubTotals == NULL!" ); + DBG_ASSERT( ptrFunctions, + "ScSubTotalParam::SetSubTotals(): ptrFunctions == NULL!" ); + DBG_ASSERT( (nCount > 0), + "ScSubTotalParam::SetSubTotals(): nCount <= 0!" ); + + if ( ptrSubTotals && ptrFunctions && (nCount > 0) && (nGroup <= MAXSUBTOTAL) ) + { + // 0 wird als 1 aufgefasst, sonst zum Array-Index dekrementieren + if (nGroup != 0) + nGroup--; + + delete [] pSubTotals[nGroup]; + delete [] pFunctions[nGroup]; + + pSubTotals[nGroup] = new SCCOL [nCount]; + pFunctions[nGroup] = new ScSubTotalFunc [nCount]; + nSubTotals[nGroup] = static_cast<SCCOL>(nCount); + + for ( USHORT i=0; i<nCount; i++ ) + { + pSubTotals[nGroup][i] = ptrSubTotals[i]; + pFunctions[nGroup][i] = ptrFunctions[i]; + } + } +} + +//------------------------------------------------------------------------ +// struct ScConsolidateParam: + +ScConsolidateParam::ScConsolidateParam() : + ppDataAreas( NULL ) +{ + Clear(); +} + +//------------------------------------------------------------------------ + +ScConsolidateParam::ScConsolidateParam( const ScConsolidateParam& r ) : + nCol(r.nCol),nRow(r.nRow),nTab(r.nTab), + eFunction(r.eFunction),nDataAreaCount(0), + ppDataAreas( NULL ), + bByCol(r.bByCol),bByRow(r.bByRow),bReferenceData(r.bReferenceData) +{ + if ( r.nDataAreaCount > 0 ) + { + nDataAreaCount = r.nDataAreaCount; + ppDataAreas = new ScArea*[nDataAreaCount]; + for ( USHORT i=0; i<nDataAreaCount; i++ ) + ppDataAreas[i] = new ScArea( *(r.ppDataAreas[i]) ); + } +} + +//------------------------------------------------------------------------ + +__EXPORT ScConsolidateParam::~ScConsolidateParam() +{ + ClearDataAreas(); +} + +//------------------------------------------------------------------------ + +void __EXPORT ScConsolidateParam::ClearDataAreas() +{ + if ( ppDataAreas ) + { + for ( USHORT i=0; i<nDataAreaCount; i++ ) + delete ppDataAreas[i]; + delete [] ppDataAreas; + ppDataAreas = NULL; + } + nDataAreaCount = 0; +} + +//------------------------------------------------------------------------ + +void __EXPORT ScConsolidateParam::Clear() +{ + ClearDataAreas(); + + nCol = 0; + nRow = 0; + nTab = 0; + bByCol = bByRow = bReferenceData = FALSE; + eFunction = SUBTOTAL_FUNC_SUM; +} + +//------------------------------------------------------------------------ + +ScConsolidateParam& __EXPORT ScConsolidateParam::operator=( const ScConsolidateParam& r ) +{ + nCol = r.nCol; + nRow = r.nRow; + nTab = r.nTab; + bByCol = r.bByCol; + bByRow = r.bByRow; + bReferenceData = r.bReferenceData; + eFunction = r.eFunction; + SetAreas( r.ppDataAreas, r.nDataAreaCount ); + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScConsolidateParam::operator==( const ScConsolidateParam& r ) const +{ + BOOL bEqual = (nCol == r.nCol) + && (nRow == r.nRow) + && (nTab == r.nTab) + && (bByCol == r.bByCol) + && (bByRow == r.bByRow) + && (bReferenceData == r.bReferenceData) + && (nDataAreaCount == r.nDataAreaCount) + && (eFunction == r.eFunction); + + if ( nDataAreaCount == 0 ) + bEqual = bEqual && (ppDataAreas == NULL) && (r.ppDataAreas == NULL); + else + bEqual = bEqual && (ppDataAreas != NULL) && (r.ppDataAreas != NULL); + + if ( bEqual && (nDataAreaCount > 0) ) + for ( USHORT i=0; i<nDataAreaCount && bEqual; i++ ) + bEqual = *(ppDataAreas[i]) == *(r.ppDataAreas[i]); + + return bEqual; +} + +//------------------------------------------------------------------------ + +void __EXPORT ScConsolidateParam::SetAreas( ScArea* const* ppAreas, USHORT nCount ) +{ + ClearDataAreas(); + if ( ppAreas && nCount > 0 ) + { + ppDataAreas = new ScArea*[nCount]; + for ( USHORT i=0; i<nCount; i++ ) + ppDataAreas[i] = new ScArea( *(ppAreas[i]) ); + nDataAreaCount = nCount; + } +} + +// ----------------------------------------------------------------------- + +PivotField::PivotField( SCsCOL nNewCol, USHORT nNewFuncMask ) : + nCol( nNewCol ), + nFuncMask( nNewFuncMask ), + nFuncCount( 0 ) +{ +} + +bool PivotField::operator==( const PivotField& r ) const +{ + return (nCol == r.nCol) + && (nFuncMask == r.nFuncMask) + && (nFuncCount == r.nFuncCount) + && (maFieldRef.ReferenceType == r.maFieldRef.ReferenceType) + && (maFieldRef.ReferenceField == r.maFieldRef.ReferenceField) + && (maFieldRef.ReferenceItemType == r.maFieldRef.ReferenceItemType) + && (maFieldRef.ReferenceItemName == r.maFieldRef.ReferenceItemName); +} + +//------------------------------------------------------------------------ +// struct ScPivotParam: + +ScPivotParam::ScPivotParam() + : nCol(0), nRow(0), nTab(0), + ppLabelArr( NULL ), nLabels(0), + nPageCount(0), nColCount(0), nRowCount(0), nDataCount(0), + bIgnoreEmptyRows(FALSE), bDetectCategories(FALSE), + bMakeTotalCol(TRUE), bMakeTotalRow(TRUE) +{ +} + +//------------------------------------------------------------------------ + +ScPivotParam::ScPivotParam( const ScPivotParam& r ) + : nCol( r.nCol ), nRow( r.nRow ), nTab( r.nTab ), + ppLabelArr( NULL ), nLabels(0), + nPageCount(0), nColCount(0), nRowCount(0), nDataCount(0), + bIgnoreEmptyRows(r.bIgnoreEmptyRows), + bDetectCategories(r.bDetectCategories), + bMakeTotalCol(r.bMakeTotalCol), + bMakeTotalRow(r.bMakeTotalRow) +{ + SetLabelData ( r.ppLabelArr, r.nLabels ); + SetPivotArrays ( r.aPageArr, r.aColArr, r.aRowArr, r.aDataArr, + r.nPageCount, r.nColCount, r.nRowCount, r.nDataCount ); +} + +//------------------------------------------------------------------------ + +__EXPORT ScPivotParam::~ScPivotParam() +{ + ClearLabelData(); +} + +//------------------------------------------------------------------------ + +//UNUSED2009-05 void __EXPORT ScPivotParam::Clear() +//UNUSED2009-05 { +//UNUSED2009-05 nCol = 0; +//UNUSED2009-05 nRow = 0; +//UNUSED2009-05 nTab = 0; +//UNUSED2009-05 bIgnoreEmptyRows = bDetectCategories = FALSE; +//UNUSED2009-05 bMakeTotalCol = bMakeTotalRow = TRUE; +//UNUSED2009-05 ClearLabelData(); +//UNUSED2009-05 ClearPivotArrays(); +//UNUSED2009-05 } + +//------------------------------------------------------------------------ + +void __EXPORT ScPivotParam::ClearLabelData() +{ + if ( (nLabels > 0) && ppLabelArr ) + { + for ( SCSIZE i=0; i<nLabels; i++ ) + delete ppLabelArr[i]; + delete [] ppLabelArr; + ppLabelArr = NULL; + nLabels = 0; + } +} + +//------------------------------------------------------------------------ + +void __EXPORT ScPivotParam::ClearPivotArrays() +{ + memset( aPageArr, 0, PIVOT_MAXPAGEFIELD * sizeof(PivotField) ); + memset( aColArr, 0, PIVOT_MAXFIELD * sizeof(PivotField) ); + memset( aRowArr, 0, PIVOT_MAXFIELD * sizeof(PivotField) ); + memset( aDataArr, 0, PIVOT_MAXFIELD * sizeof(PivotField) ); + nPageCount = 0; + nColCount = 0; + nRowCount = 0; + nDataCount = 0; +} + +//------------------------------------------------------------------------ + +void __EXPORT ScPivotParam::SetLabelData( LabelData** pLabArr, + SCSIZE nLab ) +{ + ClearLabelData(); + + if ( (nLab > 0) && pLabArr ) + { + nLabels = (nLab>MAX_LABELS) ? MAX_LABELS : nLab; + ppLabelArr = new LabelData*[nLabels]; + for ( SCSIZE i=0; i<nLabels; i++ ) + ppLabelArr[i] = new LabelData( *(pLabArr[i]) ); + } +} + +//------------------------------------------------------------------------ + +void __EXPORT ScPivotParam::SetPivotArrays ( const PivotField* pPageArr, + const PivotField* pColArr, + const PivotField* pRowArr, + const PivotField* pDataArr, + SCSIZE nPageCnt, + SCSIZE nColCnt, + SCSIZE nRowCnt, + SCSIZE nDataCnt ) +{ + ClearPivotArrays(); + + if ( pPageArr && pColArr && pRowArr && pDataArr ) + { + nPageCount = (nPageCnt>PIVOT_MAXPAGEFIELD) ? PIVOT_MAXPAGEFIELD : nPageCnt; + nColCount = (nColCnt>PIVOT_MAXFIELD) ? PIVOT_MAXFIELD : nColCnt; + nRowCount = (nRowCnt>PIVOT_MAXFIELD) ? PIVOT_MAXFIELD : nRowCnt; + nDataCount = (nDataCnt>PIVOT_MAXFIELD) ? PIVOT_MAXFIELD : nDataCnt; + + memcpy( aPageArr, pPageArr, nPageCount * sizeof(PivotField) ); + memcpy( aColArr, pColArr, nColCount * sizeof(PivotField) ); + memcpy( aRowArr, pRowArr, nRowCount * sizeof(PivotField) ); + memcpy( aDataArr, pDataArr, nDataCount * sizeof(PivotField) ); + } +} + +//------------------------------------------------------------------------ + +ScPivotParam& __EXPORT ScPivotParam::operator=( const ScPivotParam& r ) +{ + nCol = r.nCol; + nRow = r.nRow; + nTab = r.nTab; + bIgnoreEmptyRows = r.bIgnoreEmptyRows; + bDetectCategories = r.bDetectCategories; + bMakeTotalCol = r.bMakeTotalCol; + bMakeTotalRow = r.bMakeTotalRow; + + SetLabelData ( r.ppLabelArr, r.nLabels ); + SetPivotArrays ( r.aPageArr, r.aColArr, r.aRowArr, r.aDataArr, + r.nPageCount, r.nColCount, r.nRowCount, r.nDataCount ); + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScPivotParam::operator==( const ScPivotParam& r ) const +{ + BOOL bEqual = (nCol == r.nCol) + && (nRow == r.nRow) + && (nTab == r.nTab) + && (bIgnoreEmptyRows == r.bIgnoreEmptyRows) + && (bDetectCategories == r.bDetectCategories) + && (bMakeTotalCol == r.bMakeTotalCol) + && (bMakeTotalRow == r.bMakeTotalRow) + && (nLabels == r.nLabels) + && (nPageCount == r.nPageCount) + && (nColCount == r.nColCount) + && (nRowCount == r.nRowCount) + && (nDataCount == r.nDataCount); + + if ( bEqual ) + { + SCSIZE i; + + for ( i=0; i<nPageCount && bEqual; i++ ) + bEqual = ( aPageArr[i] == r.aPageArr[i] ); + + for ( i=0; i<nColCount && bEqual; i++ ) + bEqual = ( aColArr[i] == r.aColArr[i] ); + + for ( i=0; i<nRowCount && bEqual; i++ ) + bEqual = ( aRowArr[i] == r.aRowArr[i] ); + + for ( i=0; i<nDataCount && bEqual; i++ ) + bEqual = ( aDataArr[i] == r.aDataArr[i] ); + } + + return bEqual; +} + +//------------------------------------------------------------------------ +// struct ScSolveParam + +ScSolveParam::ScSolveParam() + : pStrTargetVal( NULL ) +{ +} + +//------------------------------------------------------------------------ + +ScSolveParam::ScSolveParam( const ScSolveParam& r ) + : aRefFormulaCell ( r.aRefFormulaCell ), + aRefVariableCell( r.aRefVariableCell ), + pStrTargetVal ( r.pStrTargetVal + ? new String(*r.pStrTargetVal) + : NULL ) +{ +} + +//------------------------------------------------------------------------ + +ScSolveParam::ScSolveParam( const ScAddress& rFormulaCell, + const ScAddress& rVariableCell, + const String& rTargetValStr ) + : aRefFormulaCell ( rFormulaCell ), + aRefVariableCell( rVariableCell ), + pStrTargetVal ( new String(rTargetValStr) ) +{ +} + +//------------------------------------------------------------------------ + +ScSolveParam::~ScSolveParam() +{ + delete pStrTargetVal; +} + +//------------------------------------------------------------------------ + +ScSolveParam& __EXPORT ScSolveParam::operator=( const ScSolveParam& r ) +{ + delete pStrTargetVal; + + aRefFormulaCell = r.aRefFormulaCell; + aRefVariableCell = r.aRefVariableCell; + pStrTargetVal = r.pStrTargetVal + ? new String(*r.pStrTargetVal) + : NULL; + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScSolveParam::operator==( const ScSolveParam& r ) const +{ + BOOL bEqual = (aRefFormulaCell == r.aRefFormulaCell) + && (aRefVariableCell == r.aRefVariableCell); + + if ( bEqual ) + { + if ( !pStrTargetVal && !r.pStrTargetVal ) + bEqual = TRUE; + else if ( !pStrTargetVal || !r.pStrTargetVal ) + bEqual = FALSE; + else if ( pStrTargetVal && r.pStrTargetVal ) + bEqual = ( *pStrTargetVal == *(r.pStrTargetVal) ); + } + + return bEqual; +} + + +//------------------------------------------------------------------------ +// struct ScTabOpParam + +ScTabOpParam::ScTabOpParam( const ScTabOpParam& r ) + : aRefFormulaCell ( r.aRefFormulaCell ), + aRefFormulaEnd ( r.aRefFormulaEnd ), + aRefRowCell ( r.aRefRowCell ), + aRefColCell ( r.aRefColCell ), + nMode ( r.nMode ) +{ +} + +//------------------------------------------------------------------------ + +ScTabOpParam::ScTabOpParam( const ScRefAddress& rFormulaCell, + const ScRefAddress& rFormulaEnd, + const ScRefAddress& rRowCell, + const ScRefAddress& rColCell, + BYTE nMd) + : aRefFormulaCell ( rFormulaCell ), + aRefFormulaEnd ( rFormulaEnd ), + aRefRowCell ( rRowCell ), + aRefColCell ( rColCell ), + nMode ( nMd ) +{ +} + +//------------------------------------------------------------------------ + +ScTabOpParam& ScTabOpParam::operator=( const ScTabOpParam& r ) +{ + aRefFormulaCell = r.aRefFormulaCell; + aRefFormulaEnd = r.aRefFormulaEnd; + aRefRowCell = r.aRefRowCell; + aRefColCell = r.aRefColCell; + nMode = r.nMode; + return *this; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScTabOpParam::operator==( const ScTabOpParam& r ) const +{ + return ( (aRefFormulaCell == r.aRefFormulaCell) + && (aRefFormulaEnd == r.aRefFormulaEnd) + && (aRefRowCell == r.aRefRowCell) + && (aRefColCell == r.aRefColCell) + && (nMode == r.nMode) ); +} + +String ScGlobal::GetAbsDocName( const String& rFileName, + SfxObjectShell* pShell ) +{ + String aAbsName; + if ( !pShell->HasName() ) + { // maybe relative to document path working directory + INetURLObject aObj; + SvtPathOptions aPathOpt; + aObj.SetSmartURL( aPathOpt.GetWorkPath() ); + aObj.setFinalSlash(); // it IS a path + bool bWasAbs = true; + aAbsName = aObj.smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::NO_DECODE); + // returned string must be encoded because it's used directly to create SfxMedium + } + else + { + const SfxMedium* pMedium = pShell->GetMedium(); + if ( pMedium ) + { + bool bWasAbs = true; + aAbsName = pMedium->GetURLObject().smartRel2Abs( rFileName, bWasAbs ).GetMainURL(INetURLObject::NO_DECODE); + } + else + { // This can't happen, but ... + // just to be sure to have the same encoding + INetURLObject aObj; + aObj.SetSmartURL( aAbsName ); + aAbsName = aObj.GetMainURL(INetURLObject::NO_DECODE); + } + } + return aAbsName; +} + + +String ScGlobal::GetDocTabName( const String& rFileName, + const String& rTabName ) +{ + String aDocTab( '\'' ); + aDocTab += rFileName; + xub_StrLen nPos = 1; + while( (nPos = aDocTab.Search( '\'', nPos )) + != STRING_NOTFOUND ) + { // escape Quotes + aDocTab.Insert( '\\', nPos ); + nPos += 2; + } + aDocTab += '\''; + aDocTab += SC_COMPILER_FILE_TAB_SEP; + aDocTab += rTabName; // "'Doc'#Tab" + return aDocTab; +} + +// ============================================================================ + +ScSimpleSharedString::StringTable::StringTable() : + mnStrCount(0) +{ + // empty string (ID = 0) + maSharedStrings.push_back(String()); + maSharedStringIds.insert( SharedStrMap::value_type(String(), mnStrCount++) ); +} + +ScSimpleSharedString::StringTable::StringTable(const ScSimpleSharedString::StringTable& r) : + maSharedStrings(r.maSharedStrings), + maSharedStringIds(r.maSharedStringIds), + mnStrCount(r.mnStrCount) +{ +} + +ScSimpleSharedString::StringTable::~StringTable() +{ +} + +sal_Int32 ScSimpleSharedString::StringTable::insertString(const String& aStr) +{ + SharedStrMap::const_iterator itr = maSharedStringIds.find(aStr), + itrEnd = maSharedStringIds.end(); + + if (itr == itrEnd) + { + // new string. + maSharedStrings.push_back(aStr); + maSharedStringIds.insert( SharedStrMap::value_type(aStr, mnStrCount) ); + return mnStrCount++; + } + + // existing string. + return itr->second; +} + +sal_Int32 ScSimpleSharedString::StringTable::getStringId(const String& aStr) +{ + SharedStrMap::const_iterator itr = maSharedStringIds.find(aStr), + itrEnd = maSharedStringIds.end(); + if (itr == itrEnd) + { + // string not found. + return insertString(aStr); + } + return itr->second; +} + +const String* ScSimpleSharedString::StringTable::getString(sal_Int32 nId) const +{ + if (nId >= mnStrCount) + return NULL; + + return &maSharedStrings[nId]; +} + +// ---------------------------------------------------------------------------- + +ScSimpleSharedString::ScSimpleSharedString() +{ +} + +ScSimpleSharedString::ScSimpleSharedString(const ScSimpleSharedString& r) : + maStringTable(r.maStringTable) +{ +} + +ScSimpleSharedString::~ScSimpleSharedString() +{ +} + +sal_Int32 ScSimpleSharedString::insertString(const String& aStr) +{ + return maStringTable.insertString(aStr); +} + +const String* ScSimpleSharedString::getString(sal_Int32 nId) +{ + return maStringTable.getString(nId); +} + +sal_Int32 ScSimpleSharedString::getStringId(const String& aStr) +{ + return maStringTable.getStringId(aStr); +} diff --git a/sc/source/core/data/globalx.cxx b/sc/source/core/data/globalx.cxx new file mode 100644 index 000000000000..03ca187df4d2 --- /dev/null +++ b/sc/source/core/data/globalx.cxx @@ -0,0 +1,174 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: globalx.cxx,v $ + * $Revision: 1.14 $ + * + * 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 "callform.hxx" +#include "global.hxx" +#include <tools/urlobj.hxx> +#include <ucbhelper/contentbroker.hxx> +#include <ucbhelper/content.hxx> +#include <unotools/localfilehelper.hxx> + +#include <tools/debug.hxx> +#include <svtools/pathoptions.hxx> + +#include <com/sun/star/sdbc/XResultSet.hpp> +#include <com/sun/star/sdbc/XRow.hpp> +#include <com/sun/star/ucb/XCommandEnvironment.hpp> +#include <com/sun/star/ucb/XContentAccess.hpp> + +#include <com/sun/star/i18n/XOrdinalSuffix.hpp> +#include <com/sun/star/lang/XMultiServiceFactory.hpp> +#include <comphelper/processfactory.hxx> +#include <unotools/localedatawrapper.hxx> + + +using namespace ::com::sun::star; +using namespace ::com::sun::star::uno; +using namespace ::com::sun::star::ucb; + + +// static +void ScGlobal::InitAddIns() +{ + // multi paths separated by semicolons + SvtPathOptions aPathOpt; + String aMultiPath = aPathOpt.GetAddinPath(); + if ( aMultiPath.Len() > 0 ) + { + xub_StrLen nTokens = aMultiPath.GetTokenCount( ';' ); + xub_StrLen nIndex = 0; + for ( xub_StrLen j=0; j<nTokens; j++ ) + { + String aPath( aMultiPath.GetToken( 0, ';', nIndex ) ); + if ( aPath.Len() > 0 ) + { + // use LocalFileHelper to convert the path to a URL that always points + // to the file on the server + String aUrl; + if ( utl::LocalFileHelper::ConvertPhysicalNameToURL( aPath, aUrl ) ) + aPath = aUrl; + + INetURLObject aObj; + aObj.SetSmartURL( aPath ); + aObj.setFinalSlash(); + try + { + ::ucbhelper::Content aCnt( aObj.GetMainURL(INetURLObject::NO_DECODE), + Reference< XCommandEnvironment > () ); + Reference< sdbc::XResultSet > xResultSet; + Sequence< rtl::OUString > aProps; + try + { + xResultSet = aCnt.createCursor( + aProps, ::ucbhelper::INCLUDE_DOCUMENTS_ONLY ); + } + catch ( Exception& ) + { + // ucb may throw different exceptions on failure now + // no assertion if AddIn directory doesn't exist + } + + if ( xResultSet.is() ) + { + Reference< sdbc::XRow > xRow( xResultSet, UNO_QUERY ); + Reference< XContentAccess > + xContentAccess( xResultSet, UNO_QUERY ); + try + { + if ( xResultSet->first() ) + { + do + { + rtl::OUString aId( xContentAccess->queryContentIdentifierString() ); + InitExternalFunc( aId ); + } + while ( xResultSet->next() ); + } + } + catch ( Exception& ) + { + DBG_ERRORFILE( "ResultSetException catched!" ); + } + } + } + catch ( Exception& ) + { + DBG_ERRORFILE( "Exception catched!" ); + } + catch ( ... ) + { + + DBG_ERRORFILE( "unexpected exception caught!" ); + } + } + } + } +} + + +// static +String ScGlobal::GetOrdinalSuffix( sal_Int32 nNumber) +{ + if (!xOrdinalSuffix.is()) + { + try + { + Reference< lang::XMultiServiceFactory > xServiceManager = + ::comphelper::getProcessServiceFactory(); + Reference< XInterface > xInterface = + xServiceManager->createInstance( + ::rtl::OUString::createFromAscii("com.sun.star.i18n.OrdinalSuffix")); + if (xInterface.is()) + xOrdinalSuffix = Reference< i18n::XOrdinalSuffix >( xInterface, UNO_QUERY); + } + catch ( Exception& ) + { + DBG_ERRORFILE( "GetOrdinalSuffix: exception caught during init" ); + } + } + DBG_ASSERT( xOrdinalSuffix.is(), "GetOrdinalSuffix: createInstance failed"); + if (xOrdinalSuffix.is()) + { + try + { + return xOrdinalSuffix->getOrdinalSuffix( nNumber, + ScGlobal::pLocaleData->getLocale()); + } + catch ( Exception& ) + { + DBG_ERRORFILE( "GetOrdinalSuffix: exception caught during getOrdinalSuffix" ); + } + } + return String(); +} diff --git a/sc/source/core/data/makefile.mk b/sc/source/core/data/makefile.mk new file mode 100644 index 000000000000..d2c700c5f95d --- /dev/null +++ b/sc/source/core/data/makefile.mk @@ -0,0 +1,171 @@ +#************************************************************************* +# +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# Copyright 2008 by Sun Microsystems, Inc. +# +# OpenOffice.org - a multi-platform office productivity suite +# +# $RCSfile: makefile.mk,v $ +# +# $Revision: 1.26.100.1 $ +# +# 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. +# +#************************************************************************* + +PRJ=..$/..$/.. + +PRJNAME=sc +TARGET=data + +PROJECTPCH4DLL=TRUE +PROJECTPCH=core_pch +PROJECTPCHSOURCE=..\pch\core_pch + +AUTOSEG=true + +# --- Settings ----------------------------------------------------- + +.INCLUDE : scpre.mk +.INCLUDE : settings.mk +.INCLUDE : sc.mk +.INCLUDE : $(PRJ)$/util$/makefile.pmk + +# --- Files -------------------------------------------------------- + +SLOFILES = \ + $(SLO)$/attarray.obj \ + $(SLO)$/attrib.obj \ + $(SLO)$/autonamecache.obj \ + $(SLO)$/bcaslot.obj \ + $(SLO)$/cell.obj \ + $(SLO)$/cell2.obj \ + $(SLO)$/clipparam.obj \ + $(SLO)$/column.obj \ + $(SLO)$/column2.obj \ + $(SLO)$/column3.obj \ + $(SLO)$/compressedarray.obj \ + $(SLO)$/conditio.obj \ + $(SLO)$/dbdocutl.obj \ + $(SLO)$/dociter.obj \ + $(SLO)$/docpool.obj \ + $(SLO)$/documen2.obj \ + $(SLO)$/documen3.obj \ + $(SLO)$/documen4.obj \ + $(SLO)$/documen5.obj \ + $(SLO)$/documen6.obj \ + $(SLO)$/documen7.obj \ + $(SLO)$/documen8.obj \ + $(SLO)$/documen9.obj \ + $(SLO)$/document.obj \ + $(SLO)$/dpcachetable.obj \ + $(SLO)$/dpdimsave.obj \ + $(SLO)$/dpgroup.obj \ + $(SLO)$/dpobject.obj \ + $(SLO)$/dpoutput.obj \ + $(SLO)$/dpsave.obj \ + $(SLO)$/dpsdbtab.obj \ + $(SLO)$/dpshttab.obj \ + $(SLO)$/dptabdat.obj \ + $(SLO)$/dptabres.obj \ + $(SLO)$/dptabsrc.obj \ + $(SLO)$/drawpage.obj \ + $(SLO)$/drwlayer.obj \ + $(SLO)$/fillinfo.obj \ + $(SLO)$/global.obj \ + $(SLO)$/global2.obj \ + $(SLO)$/globalx.obj \ + $(SLO)$/markarr.obj \ + $(SLO)$/markdata.obj \ + $(SLO)$/olinetab.obj \ + $(SLO)$/pagepar.obj \ + $(SLO)$/patattr.obj \ + $(SLO)$/pivot.obj \ + $(SLO)$/pivot2.obj \ + $(SLO)$/poolhelp.obj \ + $(SLO)$/scimpexpmsg.obj \ + $(SLO)$/sortparam.obj \ + $(SLO)$/stlpool.obj \ + $(SLO)$/stlsheet.obj \ + $(SLO)$/table1.obj \ + $(SLO)$/table2.obj \ + $(SLO)$/table3.obj \ + $(SLO)$/table4.obj \ + $(SLO)$/table5.obj \ + $(SLO)$/table6.obj \ + $(SLO)$/tabprotection.obj \ + $(SLO)$/userdat.obj \ + $(SLO)$/validat.obj \ + $(SLO)$/postit.obj + +EXCEPTIONSFILES= \ + $(SLO)$/autonamecache.obj \ + $(SLO)$/bcaslot.obj \ + $(SLO)$/cell2.obj \ + $(SLO)$/clipparam.obj \ + $(SLO)$/column.obj \ + $(SLO)$/column3.obj \ + $(SLO)$/documen2.obj \ + $(SLO)$/document.obj \ + $(SLO)$/dpdimsave.obj \ + $(SLO)$/dpgroup.obj \ + $(SLO)$/dpshttab.obj \ + $(SLO)$/dptabres.obj \ + $(SLO)$/dptabdat.obj \ + $(SLO)$/global2.obj \ + $(SLO)$/table1.obj \ + $(SLO)$/table2.obj \ + $(SLO)$/table3.obj \ + $(SLO)$/tabprotection.obj \ + $(SLO)$/postit.obj \ + $(SLO)$/documen3.obj \ + $(SLO)$/documen5.obj \ + $(SLO)$/documen6.obj \ + $(SLO)$/documen9.obj \ + $(SLO)$/dpcachetable.obj \ + $(SLO)$/dpsdbtab.obj \ + $(SLO)$/dpobject.obj \ + $(SLO)$/dpoutput.obj \ + $(SLO)$/dpsave.obj \ + $(SLO)$/dbdocutl.obj \ + $(SLO)$/dptabsrc.obj \ + $(SLO)$/drwlayer.obj \ + $(SLO)$/globalx.obj + +.IF "$(OS)$(COM)$(CPUNAME)"=="LINUXGCCSPARC" +NOOPTFILES= \ + $(SLO)$/column2.obj \ + $(SLO)$/column3.obj \ + $(SLO)$/table3.obj \ + $(SLO)$/table4.obj \ + $(SLO)$/documen4.obj \ + $(SLO)$/conditio.obj \ + $(SLO)$/validat.obj +EXCEPTIONSNOOPTFILES= \ + $(SLO)$/cell.obj +.ELSE +EXCEPTIONSFILES+= \ + $(SLO)$/cell.obj \ + $(SLO)$/global.obj +.ENDIF + +# --- Tagets ------------------------------------------------------- + +.INCLUDE : target.mk + diff --git a/sc/source/core/data/markarr.cxx b/sc/source/core/data/markarr.cxx new file mode 100644 index 000000000000..362c7e769b7a --- /dev/null +++ b/sc/source/core/data/markarr.cxx @@ -0,0 +1,413 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: markarr.cxx,v $ + * $Revision: 1.11.32.1 $ + * + * 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 <tools/debug.hxx> + +#include "markarr.hxx" +#include "global.hxx" +#include "address.hxx" + +// STATIC DATA ----------------------------------------------------------- + +//------------------------------------------------------------------------ + +ScMarkArray::ScMarkArray() : + nCount( 0 ), + nLimit( 0 ), + pData( NULL ) +{ + // special case "no marks" with pData = NULL +} + +//------------------------------------------------------------------------ + +ScMarkArray::~ScMarkArray() +{ + delete[] pData; +} + +//------------------------------------------------------------------------ + +void ScMarkArray::Reset( BOOL bMarked ) +{ + // always create pData here + // (or have separate method to ensure pData) + + delete[] pData; + + nCount = nLimit = 1; + pData = new ScMarkEntry[1]; + pData[0].nRow = MAXROW; + pData[0].bMarked = bMarked; +} + +//------------------------------------------------------------------------ + +BOOL ScMarkArray::Search( SCROW nRow, SCSIZE& nIndex ) const +{ + long nLo = 0; + long nHi = static_cast<long>(nCount) - 1; + long nStartRow = 0; + long nEndRow = 0; + long i = 0; + BOOL bFound = (nCount == 1); + if (pData) + { + while ( !bFound && nLo <= nHi ) + { + i = (nLo + nHi) / 2; + if (i > 0) + nStartRow = (long) pData[i - 1].nRow; + else + nStartRow = -1; + nEndRow = (long) pData[i].nRow; + if (nEndRow < (long) nRow) + nLo = ++i; + else + if (nStartRow >= (long) nRow) + nHi = --i; + else + bFound = TRUE; + } + } + else + bFound = FALSE; + + if (bFound) + nIndex=(SCSIZE)i; + else + nIndex=0; + return bFound; +} + +BOOL ScMarkArray::GetMark( SCROW nRow ) const +{ + SCSIZE i; + if (Search( nRow, i )) + return pData[i].bMarked; + else + return FALSE; + +} + +//------------------------------------------------------------------------ + +void ScMarkArray::SetMarkArea( SCROW nStartRow, SCROW nEndRow, BOOL bMarked ) +{ + if (ValidRow(nStartRow) && ValidRow(nEndRow)) + { + if ((nStartRow == 0) && (nEndRow == MAXROW)) + { + Reset(bMarked); + } + else + { + if (!pData) + Reset(FALSE); // create pData for further processing - could use special case handling! + + SCSIZE nNeeded = nCount + 2; + if ( nLimit < nNeeded ) + { + nLimit += SC_MARKARRAY_DELTA; + if ( nLimit < nNeeded ) + nLimit = nNeeded; + ScMarkEntry* pNewData = new ScMarkEntry[nLimit]; + memcpy( pNewData, pData, nCount*sizeof(ScMarkEntry) ); + delete[] pData; + pData = pNewData; + } + + SCSIZE ni; // number of entries in beginning + SCSIZE nInsert; // insert position (MAXROW+1 := no insert) + BOOL bCombined = FALSE; + BOOL bSplit = FALSE; + if ( nStartRow > 0 ) + { + // skip beginning + SCSIZE nIndex; + Search( nStartRow, nIndex ); + ni = nIndex; + + nInsert = MAXROWCOUNT; + if ( pData[ni].bMarked != bMarked ) + { + if ( ni == 0 || (pData[ni-1].nRow < nStartRow - 1) ) + { // may be a split or a simple insert or just a shrink, + // row adjustment is done further down + if ( pData[ni].nRow > nEndRow ) + bSplit = TRUE; + ni++; + nInsert = ni; + } + else if ( ni > 0 && pData[ni-1].nRow == nStartRow - 1 ) + nInsert = ni; + } + if ( ni > 0 && pData[ni-1].bMarked == bMarked ) + { // combine + pData[ni-1].nRow = nEndRow; + nInsert = MAXROWCOUNT; + bCombined = TRUE; + } + } + else + { + nInsert = 0; + ni = 0; + } + + SCSIZE nj = ni; // stop position of range to replace + while ( nj < nCount && pData[nj].nRow <= nEndRow ) + nj++; + if ( !bSplit ) + { + if ( nj < nCount && pData[nj].bMarked == bMarked ) + { // combine + if ( ni > 0 ) + { + if ( pData[ni-1].bMarked == bMarked ) + { // adjacent entries + pData[ni-1].nRow = pData[nj].nRow; + nj++; + } + else if ( ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + nInsert = MAXROWCOUNT; + bCombined = TRUE; + } + else if ( ni > 0 && ni == nInsert ) + pData[ni-1].nRow = nStartRow - 1; // shrink + } + if ( ni < nj ) + { // remove middle entries + if ( !bCombined ) + { // replace one entry + pData[ni].nRow = nEndRow; + pData[ni].bMarked = bMarked; + ni++; + nInsert = MAXROWCOUNT; + } + if ( ni < nj ) + { // remove entries + memmove( pData + ni, pData + nj, (nCount - nj) * sizeof(ScMarkEntry) ); + nCount -= nj - ni; + } + } + + if ( nInsert < sal::static_int_cast<SCSIZE>(MAXROWCOUNT) ) + { // insert or append new entry + if ( nInsert <= nCount ) + { + if ( !bSplit ) + memmove( pData + nInsert + 1, pData + nInsert, + (nCount - nInsert) * sizeof(ScMarkEntry) ); + else + { + memmove( pData + nInsert + 2, pData + nInsert, + (nCount - nInsert) * sizeof(ScMarkEntry) ); + pData[nInsert+1] = pData[nInsert-1]; + nCount++; + } + } + if ( nInsert ) + pData[nInsert-1].nRow = nStartRow - 1; + pData[nInsert].nRow = nEndRow; + pData[nInsert].bMarked = bMarked; + nCount++; + } + } + } +// InfoBox(0, String(nCount) + String(" Eintraege") ).Execute(); +} + +//UNUSED2009-05 void ScMarkArray::DeleteArea(SCROW nStartRow, SCROW nEndRow) +//UNUSED2009-05 { +//UNUSED2009-05 SetMarkArea(nStartRow, nEndRow, FALSE); +//UNUSED2009-05 } + +BOOL ScMarkArray::IsAllMarked( SCROW nStartRow, SCROW nEndRow ) const +{ + SCSIZE nStartIndex; + SCSIZE nEndIndex; + + if (Search( nStartRow, nStartIndex )) + if (pData[nStartIndex].bMarked) + if (Search( nEndRow, nEndIndex )) + if (nEndIndex==nStartIndex) + return TRUE; + + return FALSE; +} + +BOOL ScMarkArray::HasOneMark( SCROW& rStartRow, SCROW& rEndRow ) const +{ + BOOL bRet = FALSE; + if ( nCount == 1 ) + { + if ( pData[0].bMarked ) + { + rStartRow = 0; + rEndRow = MAXROW; + bRet = TRUE; + } + } + else if ( nCount == 2 ) + { + if ( pData[0].bMarked ) + { + rStartRow = 0; + rEndRow = pData[0].nRow; + } + else + { + rStartRow = pData[0].nRow + 1; + rEndRow = MAXROW; + } + bRet = TRUE; + } + else if ( nCount == 3 ) + { + if ( pData[1].bMarked ) + { + rStartRow = pData[0].nRow + 1; + rEndRow = pData[1].nRow; + bRet = TRUE; + } + } + return bRet; +} + +void ScMarkArray::CopyMarksTo( ScMarkArray& rDestMarkArray ) const +{ + delete[] rDestMarkArray.pData; + + if (pData) + { + rDestMarkArray.pData = new ScMarkEntry[nCount]; + memmove( rDestMarkArray.pData, pData, nCount * sizeof(ScMarkEntry) ); + } + else + rDestMarkArray.pData = NULL; + + rDestMarkArray.nCount = rDestMarkArray.nLimit = nCount; +} + +SCsROW ScMarkArray::GetNextMarked( SCsROW nRow, BOOL bUp ) const +{ + if (!pData) + const_cast<ScMarkArray*>(this)->Reset(FALSE); // create pData for further processing + + SCsROW nRet = nRow; + if (VALIDROW(nRow)) + { + SCSIZE nIndex; + Search(nRow, nIndex); + if (!pData[nIndex].bMarked) + { + if (bUp) + { + if (nIndex>0) + nRet = pData[nIndex-1].nRow; + else + nRet = -1; + } + else + nRet = pData[nIndex].nRow + 1; + } + } + return nRet; +} + +SCROW ScMarkArray::GetMarkEnd( SCROW nRow, BOOL bUp ) const +{ + if (!pData) + const_cast<ScMarkArray*>(this)->Reset(FALSE); // create pData for further processing + + SCROW nRet; + SCSIZE nIndex; + Search(nRow, nIndex); + DBG_ASSERT( pData[nIndex].bMarked, "GetMarkEnd ohne bMarked" ); + if (bUp) + { + if (nIndex>0) + nRet = pData[nIndex-1].nRow + 1; + else + nRet = 0; + } + else + nRet = pData[nIndex].nRow; + + return nRet; +} + +// +// -------------- Iterator ---------------------------------------------- +// + +ScMarkArrayIter::ScMarkArrayIter( const ScMarkArray* pNewArray ) : + pArray( pNewArray ), + nPos( 0 ) +{ +} + +ScMarkArrayIter::~ScMarkArrayIter() +{ +} + +BOOL ScMarkArrayIter::Next( SCROW& rTop, SCROW& rBottom ) +{ + if ( nPos >= pArray->nCount ) + return FALSE; + while (!pArray->pData[nPos].bMarked) + { + ++nPos; + if ( nPos >= pArray->nCount ) + return FALSE; + } + rBottom = pArray->pData[nPos].nRow; + if (nPos==0) + rTop = 0; + else + rTop = pArray->pData[nPos-1].nRow + 1; + ++nPos; + return TRUE; +} + + + + + diff --git a/sc/source/core/data/markdata.cxx b/sc/source/core/data/markdata.cxx new file mode 100644 index 000000000000..015a33928b7e --- /dev/null +++ b/sc/source/core/data/markdata.cxx @@ -0,0 +1,587 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: markdata.cxx,v $ + * $Revision: 1.8 $ + * + * 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 <tools/debug.hxx> + +#include "markdata.hxx" +#include "markarr.hxx" +#include "rangelst.hxx" + +// STATIC DATA ----------------------------------------------------------- + +//------------------------------------------------------------------------ + +ScMarkData::ScMarkData() : + pMultiSel( NULL ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + bTabMarked[i] = FALSE; + + ResetMark(); +} + +ScMarkData::ScMarkData(const ScMarkData& rData) : + aMarkRange( rData.aMarkRange ), + aMultiRange( rData.aMultiRange ), + pMultiSel( NULL ) +{ + bMarked = rData.bMarked; + bMultiMarked = rData.bMultiMarked; + bMarking = rData.bMarking; + bMarkIsNeg = rData.bMarkIsNeg; + + for (SCTAB i=0; i<=MAXTAB; i++) + bTabMarked[i] = rData.bTabMarked[i]; + + if (rData.pMultiSel) + { + pMultiSel = new ScMarkArray[MAXCOLCOUNT]; + for (SCCOL j=0; j<MAXCOLCOUNT; j++) + rData.pMultiSel[j].CopyMarksTo( pMultiSel[j] ); + } +} + +ScMarkData& ScMarkData::operator=(const ScMarkData& rData) +{ + if ( &rData == this ) + return *this; + + delete[] pMultiSel; + pMultiSel = NULL; + + aMarkRange = rData.aMarkRange; + aMultiRange = rData.aMultiRange; + bMarked = rData.bMarked; + bMultiMarked = rData.bMultiMarked; + bMarking = rData.bMarking; + bMarkIsNeg = rData.bMarkIsNeg; + + for (SCTAB i=0; i<=MAXTAB; i++) + bTabMarked[i] = rData.bTabMarked[i]; + + if (rData.pMultiSel) + { + pMultiSel = new ScMarkArray[MAXCOLCOUNT]; + for (SCCOL j=0; j<MAXCOLCOUNT; j++) + rData.pMultiSel[j].CopyMarksTo( pMultiSel[j] ); + } + + return *this; +} + +ScMarkData::~ScMarkData() +{ + delete[] pMultiSel; +} + +void ScMarkData::ResetMark() +{ + delete[] pMultiSel; + pMultiSel = NULL; + + bMarked = bMultiMarked = FALSE; + bMarking = bMarkIsNeg = FALSE; +} + +void ScMarkData::SetMarkArea( const ScRange& rRange ) +{ + aMarkRange = rRange; + aMarkRange.Justify(); + if ( !bMarked ) + { + // #77987# Upon creation of a document ScFormatShell GetTextAttrState + // may query (default) attributes although no sheet is marked yet. + // => mark that one. + if ( !GetSelectCount() ) + bTabMarked[ aMarkRange.aStart.Tab() ] = TRUE; + bMarked = TRUE; + } +} + +void ScMarkData::GetMarkArea( ScRange& rRange ) const +{ + rRange = aMarkRange; //! inline ? +} + +void ScMarkData::GetMultiMarkArea( ScRange& rRange ) const +{ + rRange = aMultiRange; +} + +void ScMarkData::SetMultiMarkArea( const ScRange& rRange, BOOL bMark ) +{ + if (!pMultiSel) + { + pMultiSel = new ScMarkArray[MAXCOL+1]; + + // if simple mark range is set, copy to multi marks + if ( bMarked && !bMarkIsNeg ) + { + bMarked = FALSE; + SetMultiMarkArea( aMarkRange, TRUE ); + } + } + + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + PutInOrder( nStartRow, nEndRow ); + PutInOrder( nStartCol, nEndCol ); + + SCCOL nCol; + for (nCol=nStartCol; nCol<=nEndCol; nCol++) + pMultiSel[nCol].SetMarkArea( nStartRow, nEndRow, bMark ); + + if ( bMultiMarked ) // aMultiRange updaten + { + if ( nStartCol < aMultiRange.aStart.Col() ) + aMultiRange.aStart.SetCol( nStartCol ); + if ( nStartRow < aMultiRange.aStart.Row() ) + aMultiRange.aStart.SetRow( nStartRow ); + if ( nEndCol > aMultiRange.aEnd.Col() ) + aMultiRange.aEnd.SetCol( nEndCol ); + if ( nEndRow > aMultiRange.aEnd.Row() ) + aMultiRange.aEnd.SetRow( nEndRow ); + } + else + { + aMultiRange = rRange; // neu + bMultiMarked = TRUE; + } +} + +void ScMarkData::SetAreaTab( SCTAB nTab ) +{ + aMarkRange.aStart.SetTab(nTab); + aMarkRange.aEnd.SetTab(nTab); + aMultiRange.aStart.SetTab(nTab); + aMultiRange.aEnd.SetTab(nTab); +} + +void ScMarkData::SelectOneTable( SCTAB nTab ) +{ + for (SCTAB i=0; i<=MAXTAB; i++) + bTabMarked[i] = ( nTab == i ); +} + +SCTAB ScMarkData::GetSelectCount() const +{ + SCTAB nCount = 0; + for (SCTAB i=0; i<=MAXTAB; i++) + if (bTabMarked[i]) + ++nCount; + + return nCount; +} + +SCTAB ScMarkData::GetFirstSelected() const +{ + for (SCTAB i=0; i<=MAXTAB; i++) + if (bTabMarked[i]) + return i; + + DBG_ERROR("GetFirstSelected: keine markiert"); + return 0; +} + +void ScMarkData::MarkToMulti() +{ + if ( bMarked && !bMarking ) + { + SetMultiMarkArea( aMarkRange, !bMarkIsNeg ); + bMarked = FALSE; + + // check if all multi mark ranges have been removed + if ( bMarkIsNeg && !HasAnyMultiMarks() ) + ResetMark(); + } +} + +void ScMarkData::MarkToSimple() +{ + if ( bMarking ) + return; + + if ( bMultiMarked && bMarked ) + MarkToMulti(); // may result in bMarked and bMultiMarked reset + + if ( bMultiMarked ) + { + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + ScRange aNew = aMultiRange; + + BOOL bOk = FALSE; + SCCOL nStartCol = aNew.aStart.Col(); + SCCOL nEndCol = aNew.aEnd.Col(); + + while ( nStartCol < nEndCol && !pMultiSel[nStartCol].HasMarks() ) + ++nStartCol; + while ( nStartCol < nEndCol && !pMultiSel[nEndCol].HasMarks() ) + --nEndCol; + + // Zeilen werden nur aus MarkArray genommen + SCROW nStartRow, nEndRow; + if ( pMultiSel[nStartCol].HasOneMark( nStartRow, nEndRow ) ) + { + bOk = TRUE; + SCROW nCmpStart, nCmpEnd; + for (SCCOL nCol=nStartCol+1; nCol<=nEndCol && bOk; nCol++) + if ( !pMultiSel[nCol].HasOneMark( nCmpStart, nCmpEnd ) + || nCmpStart != nStartRow || nCmpEnd != nEndRow ) + bOk = FALSE; + } + + if (bOk) + { + aNew.aStart.SetCol(nStartCol); + aNew.aStart.SetRow(nStartRow); + aNew.aEnd.SetCol(nEndCol); + aNew.aEnd.SetRow(nEndRow); + + ResetMark(); + aMarkRange = aNew; + bMarked = TRUE; + bMarkIsNeg = FALSE; + } + } +} + +BOOL ScMarkData::IsCellMarked( SCCOL nCol, SCROW nRow, BOOL bNoSimple ) const +{ + if ( bMarked && !bNoSimple && !bMarkIsNeg ) + if ( aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol && + aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow ) + return TRUE; + + if (bMultiMarked) + { + //! hier auf negative Markierung testen ? + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + return pMultiSel[nCol].GetMark( nRow ); + } + + return FALSE; +} + +BOOL ScMarkData::IsColumnMarked( SCCOL nCol ) const +{ + // bMarkIsNeg inzwischen auch fuer Spaltenkoepfe + //! GetMarkColumnRanges fuer komplett markierte Spalten + + if ( bMarked && !bMarkIsNeg && + aMarkRange.aStart.Col() <= nCol && aMarkRange.aEnd.Col() >= nCol && + aMarkRange.aStart.Row() == 0 && aMarkRange.aEnd.Row() == MAXROW ) + return TRUE; + + if ( bMultiMarked && pMultiSel[nCol].IsAllMarked(0,MAXROW) ) + return TRUE; + + return FALSE; +} + +BOOL ScMarkData::IsRowMarked( SCROW nRow ) const +{ + // bMarkIsNeg inzwischen auch fuer Zeilenkoepfe + //! GetMarkRowRanges fuer komplett markierte Zeilen + + if ( bMarked && !bMarkIsNeg && + aMarkRange.aStart.Col() == 0 && aMarkRange.aEnd.Col() == MAXCOL && + aMarkRange.aStart.Row() <= nRow && aMarkRange.aEnd.Row() >= nRow ) + return TRUE; + + if ( bMultiMarked ) + { + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + for (SCCOL nCol=0; nCol<=MAXCOL; nCol++) + if (!pMultiSel[nCol].GetMark(nRow)) + return FALSE; + return TRUE; + } + + return FALSE; +} + +void ScMarkData::MarkFromRangeList( const ScRangeList& rList, BOOL bReset ) +{ + if (bReset) + { + for (SCTAB i=0; i<=MAXTAB; i++) + bTabMarked[i] = FALSE; // Tabellen sind nicht in ResetMark + ResetMark(); + } + + ULONG nCount = rList.Count(); + if ( nCount == 1 && !bMarked && !bMultiMarked ) + { + ScRange aRange = *rList.GetObject(0); + SetMarkArea( aRange ); + SelectTable( aRange.aStart.Tab(), TRUE ); + } + else + { + for (ULONG i=0; i<nCount; i++) + { + ScRange aRange = *rList.GetObject(i); + SetMultiMarkArea( aRange, TRUE ); + SelectTable( aRange.aStart.Tab(), TRUE ); + } + } +} + +void ScMarkData::FillRangeListWithMarks( ScRangeList* pList, BOOL bClear ) const +{ + if (!pList) + return; + + if (bClear) + pList->RemoveAll(); + + //! bei mehreren selektierten Tabellen mehrere Ranges eintragen !!! + + if ( bMultiMarked ) + { + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + SCTAB nTab = aMultiRange.aStart.Tab(); + + SCCOL nStartCol = aMultiRange.aStart.Col(); + SCCOL nEndCol = aMultiRange.aEnd.Col(); + for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) + if (pMultiSel[nCol].HasMarks()) + { + SCROW nTop, nBottom; + ScRange aRange( nCol, 0, nTab ); + ScMarkArrayIter aMarkIter( &pMultiSel[nCol] ); + while ( aMarkIter.Next( nTop, nBottom ) ) + { + aRange.aStart.SetRow( nTop ); + aRange.aEnd.SetRow( nBottom ); + pList->Join( aRange ); + } + } + } + + if ( bMarked ) + pList->Append( aMarkRange ); +} + +void ScMarkData::ExtendRangeListTables( ScRangeList* pList ) const +{ + if (!pList) + return; + + ScRangeList aOldList(*pList); + pList->RemoveAll(); //! oder die vorhandenen unten weglassen + + for (SCTAB nTab=0; nTab<=MAXTAB; nTab++) + if (bTabMarked[nTab]) + { + ULONG nCount = aOldList.Count(); + for (ULONG i=0; i<nCount; i++) + { + ScRange aRange = *aOldList.GetObject(i); + aRange.aStart.SetTab(nTab); + aRange.aEnd.SetTab(nTab); + pList->Append( aRange ); + } + } +} + +SCCOLROW ScMarkData::GetMarkColumnRanges( SCCOLROW* pRanges ) +{ + if (bMarked) + MarkToMulti(); + + if (!bMultiMarked) + return 0; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + SCCOLROW nRangeCnt = 0; + SCCOLROW nStart = 0; + while (nStart<=MAXCOL) + { + while (nStart<MAXCOL && !pMultiSel[nStart].HasMarks()) + ++nStart; + if (pMultiSel[nStart].HasMarks()) + { + SCCOLROW nEnd = nStart; + while (nEnd<MAXCOL && pMultiSel[nEnd].HasMarks()) + ++nEnd; + if (!pMultiSel[nEnd].HasMarks()) + --nEnd; + pRanges[2*nRangeCnt ] = nStart; + pRanges[2*nRangeCnt+1] = nEnd; + ++nRangeCnt; + nStart = nEnd+1; + } + else + nStart = MAXCOL+1; + } + + return nRangeCnt; +} + +SCCOLROW ScMarkData::GetMarkRowRanges( SCCOLROW* pRanges ) +{ + if (bMarked) + MarkToMulti(); + + if (!bMultiMarked) + return 0; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + // Welche Zeilen sind markiert? + + BOOL* bRowMarked = new BOOL[MAXROW+1]; + SCROW nRow; + SCCOL nCol; + for (nRow=0; nRow<=MAXROW; nRow++) + bRowMarked[nRow] = FALSE; + + SCROW nTop, nBottom; + for (nCol=0; nCol<=MAXCOL; nCol++) + { + ScMarkArrayIter aMarkIter( &pMultiSel[nCol] ); + while (aMarkIter.Next( nTop, nBottom )) + for (nRow=nTop; nRow<=nBottom; nRow++) + bRowMarked[nRow] = TRUE; + } + + // zu Bereichen zusammenfassen + + SCCOLROW nRangeCnt = 0; + SCCOLROW nStart = 0; + while (nStart<=MAXROW) + { + while (nStart<MAXROW && !bRowMarked[nStart]) + ++nStart; + if (bRowMarked[nStart]) + { + SCCOLROW nEnd = nStart; + while (nEnd<MAXROW && bRowMarked[nEnd]) + ++nEnd; + if (!bRowMarked[nEnd]) + --nEnd; + pRanges[2*nRangeCnt ] = nStart; + pRanges[2*nRangeCnt+1] = nEnd; + ++nRangeCnt; + nStart = nEnd+1; + } + else + nStart = MAXROW+1; + } + + delete[] bRowMarked; + return nRangeCnt; +} + +BOOL ScMarkData::IsAllMarked( const ScRange& rRange ) const +{ + if ( !bMultiMarked ) + return FALSE; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + BOOL bOk = TRUE; + for (SCCOL nCol=nStartCol; nCol<=nEndCol && bOk; nCol++) + if ( !pMultiSel[nCol].IsAllMarked( nStartRow, nEndRow ) ) + bOk = FALSE; + + return bOk; +} + +SCsROW ScMarkData::GetNextMarked( SCCOL nCol, SCsROW nRow, BOOL bUp ) const +{ + if ( !bMultiMarked ) + return nRow; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + return pMultiSel[nCol].GetNextMarked( nRow, bUp ); +} + +BOOL ScMarkData::HasMultiMarks( SCCOL nCol ) const +{ + if ( !bMultiMarked ) + return FALSE; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + return pMultiSel[nCol].HasMarks(); +} + +BOOL ScMarkData::HasAnyMultiMarks() const +{ + if ( !bMultiMarked ) + return FALSE; + + DBG_ASSERT(pMultiSel, "bMultiMarked, aber pMultiSel == 0"); + + for (SCCOL nCol=0; nCol<=MAXCOL; nCol++) + if ( pMultiSel[nCol].HasMarks() ) + return TRUE; + + return FALSE; // nix +} + +void ScMarkData::InsertTab( SCTAB nTab ) +{ + for (SCTAB i=MAXTAB; i>nTab; i--) + bTabMarked[i] = bTabMarked[i-1]; + bTabMarked[nTab] = FALSE; +} + +void ScMarkData::DeleteTab( SCTAB nTab ) +{ + for (SCTAB i=nTab; i<MAXTAB; i++) + bTabMarked[i] = bTabMarked[i+1]; + bTabMarked[MAXTAB] = FALSE; +} + + + + + diff --git a/sc/source/core/data/olinetab.cxx b/sc/source/core/data/olinetab.cxx new file mode 100644 index 000000000000..11112e5774ac --- /dev/null +++ b/sc/source/core/data/olinetab.cxx @@ -0,0 +1,811 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: olinetab.cxx,v $ + * $Revision: 1.10.32.3 $ + * + * 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" + +// System - Includes ----------------------------------------------------- + + + +#include <tools/debug.hxx> +#include <limits.h> + +// INCLUDE --------------------------------------------------------------- + +#include "olinetab.hxx" +#include "global.hxx" +#include "rechead.hxx" +#include "address.hxx" + +//------------------------------------------------------------------------ + +ScOutlineEntry::ScOutlineEntry( SCCOLROW nNewStart, SCCOLROW nNewSize, BOOL bNewHidden ) : + nStart ( nNewStart ), + nSize ( nNewSize ), + bHidden ( bNewHidden ), + bVisible( TRUE ) +{ +} + +ScOutlineEntry::ScOutlineEntry( const ScOutlineEntry& rEntry ) : + ScDataObject(), + nStart ( rEntry.nStart ), + nSize ( rEntry.nSize ), + bHidden ( rEntry.bHidden ), + bVisible( rEntry.bVisible ) +{ +} + +ScDataObject* ScOutlineEntry::Clone() const +{ + return new ScOutlineEntry( *this ); +} + +void ScOutlineEntry::Move( SCsCOLROW nDelta ) +{ + SCCOLROW nNewPos = nStart + nDelta; + if (nNewPos<0) + { + DBG_ERROR("OutlineEntry < 0"); + nNewPos = 0; + } + nStart = nNewPos; +} + +void ScOutlineEntry::SetSize( SCSIZE nNewSize ) +{ + if (nNewSize>0) + nSize = nNewSize; + else + { + DBG_ERROR("ScOutlineEntry Size == 0"); + } +} + +void ScOutlineEntry::SetPosSize( SCCOLROW nNewPos, SCSIZE nNewSize ) +{ + nStart = nNewPos; + SetSize( nNewSize ); +} + +void ScOutlineEntry::SetHidden( BOOL bNewHidden ) +{ + bHidden = bNewHidden; +} + +void ScOutlineEntry::SetVisible( BOOL bNewVisible ) +{ + bVisible = bNewVisible; +} + +//------------------------------------------------------------------------ + +ScOutlineCollection::ScOutlineCollection() : + ScSortedCollection( 4,4,FALSE ) +{ +} + +inline short IntCompare( SCCOLROW nX, SCCOLROW nY ) +{ + if ( nX==nY ) return 0; + else if ( nX<nY ) return -1; + else return 1; +} + +short ScOutlineCollection::Compare(ScDataObject* pKey1, ScDataObject* pKey2) const +{ + return IntCompare( ((ScOutlineEntry*)pKey1)->GetStart(), + ((ScOutlineEntry*)pKey2)->GetStart() ); +} + +USHORT ScOutlineCollection::FindStart( SCCOLROW nMinStart ) +{ + //! binaer suchen ? + + USHORT nPos = 0; + USHORT nLocalCount = GetCount(); + while ( (nPos<nLocalCount) ? (((ScOutlineEntry*)At(nPos))->GetStart() < nMinStart) : FALSE ) + ++nPos; + + return nPos; +} + +//------------------------------------------------------------------------ + +ScOutlineArray::ScOutlineArray() : + nDepth( 0 ) +{ +} + +ScOutlineArray::ScOutlineArray( const ScOutlineArray& rArray ) : + nDepth( rArray.nDepth ) +{ + for (USHORT nLevel=0; nLevel<nDepth; nLevel++) + { + USHORT nCount = rArray.aCollections[nLevel].GetCount(); + for (USHORT nEntry=0; nEntry<nCount; nEntry++) + { + ScOutlineEntry* pEntry = (ScOutlineEntry*) rArray.aCollections[nLevel].At(nEntry); + aCollections[nLevel].Insert( new ScOutlineEntry( *pEntry ) ); + } + } +} + +void ScOutlineArray::FindEntry( SCCOLROW nSearchPos, USHORT& rFindLevel, USHORT& rFindIndex, + USHORT nMaxLevel ) +{ + rFindLevel = rFindIndex = 0; + + if (nMaxLevel > nDepth) + nMaxLevel = nDepth; + + for (USHORT nLevel=0; nLevel<nMaxLevel; nLevel++) //! rueckwaerts suchen ? + { + ScOutlineCollection* pCollect = &aCollections[nLevel]; + USHORT nCount = pCollect->GetCount(); + for (USHORT i=0; i<nCount; i++) + { + ScOutlineEntry* pEntry = (ScOutlineEntry*) pCollect->At(i); + if ( pEntry->GetStart() <= nSearchPos && pEntry->GetEnd() >= nSearchPos ) + { + rFindLevel = nLevel + 1; // naechster Level (zum Einfuegen) + rFindIndex = i; + } + } + } +} + +BOOL ScOutlineArray::Insert( SCCOLROW nStartCol, SCCOLROW nEndCol, BOOL& rSizeChanged, + BOOL bHidden, BOOL bVisible ) +{ + rSizeChanged = FALSE; + + USHORT nStartLevel; + USHORT nStartIndex; + USHORT nEndLevel; + USHORT nEndIndex; + BOOL bFound = FALSE; + + BOOL bCont; + USHORT nFindMax; + FindEntry( nStartCol, nStartLevel, nStartIndex ); // nLevel = neuer Level (alter+1) !!! + FindEntry( nEndCol, nEndLevel, nEndIndex ); + nFindMax = Max(nStartLevel,nEndLevel); + do + { + bCont = FALSE; + + if ( nStartLevel == nEndLevel && nStartIndex == nEndIndex && nStartLevel < SC_OL_MAXDEPTH ) + bFound = TRUE; + + if (!bFound) + { + if (nFindMax>0) + { + --nFindMax; + if (nStartLevel) + if ( ((ScOutlineEntry*)aCollections[nStartLevel-1].At(nStartIndex))-> + GetStart() == nStartCol ) + FindEntry( nStartCol, nStartLevel, nStartIndex, nFindMax ); + if (nEndLevel) + if ( ((ScOutlineEntry*)aCollections[nEndLevel-1].At(nEndIndex))-> + GetEnd() == nEndCol ) + FindEntry( nEndCol, nEndLevel, nEndIndex, nFindMax ); + bCont = TRUE; + } + } + } + while ( !bFound && bCont ); + + if (!bFound) + return FALSE; + + USHORT nLevel = nStartLevel; + + // untere verschieben + + BOOL bNeedSize = FALSE; + for ( short nMoveLevel = nDepth-1; nMoveLevel >= (short) nLevel; nMoveLevel-- ) + { + USHORT nCount = aCollections[nMoveLevel].GetCount(); + BOOL bMoved = FALSE; + for ( USHORT i=0; i<nCount; i += bMoved ? 0 : 1 ) + { + ScOutlineEntry* pEntry = (ScOutlineEntry*) aCollections[nMoveLevel].At(i); + SCCOLROW nEntryStart = pEntry->GetStart(); + if ( nEntryStart >= nStartCol && nEntryStart <= nEndCol ) + { + if (nMoveLevel >= SC_OL_MAXDEPTH - 1) + { + rSizeChanged = FALSE; // kein Platz + return FALSE; + } + aCollections[nMoveLevel+1].Insert( new ScOutlineEntry( *pEntry ) ); + aCollections[nMoveLevel].AtFree( i ); + nCount = aCollections[nMoveLevel].GetCount(); + bMoved = TRUE; + if (nMoveLevel == (short) nDepth - 1) + bNeedSize = TRUE; + } + else + bMoved = FALSE; + } + } + + if (bNeedSize) + { + ++nDepth; + rSizeChanged = TRUE; + } + + if (nDepth <= nLevel) + { + nDepth = nLevel+1; + rSizeChanged = TRUE; + } + +/* nicht zusammenfassen! + + // zusammenfassen + + USHORT nCount = aCollections[nLevel].GetCount(); + USHORT nIndex; + bFound = FALSE; + for ( nIndex=0; nIndex<nCount && !bFound; nIndex++ ) + { + if ( ((ScOutlineEntry*) aCollections[nLevel].At(nIndex))->GetEnd() + 1 == nStartCol ) + { + nStartCol = ((ScOutlineEntry*) aCollections[nLevel].At(nIndex))->GetStart(); + aCollections[nLevel].AtFree(nIndex); + nCount = aCollections[nLevel].GetCount(); // Daten geaendert + bFound = TRUE; + } + } + + bFound = FALSE; + for ( nIndex=0; nIndex<nCount && !bFound; nIndex++ ) + { + if ( ((ScOutlineEntry*) aCollections[nLevel].At(nIndex))->GetStart() == nEndCol + 1 ) + { + nEndCol = ((ScOutlineEntry*) aCollections[nLevel].At(nIndex))->GetEnd(); + aCollections[nLevel].AtFree(nIndex); + bFound = TRUE; + } + } +*/ + ScOutlineEntry* pNewEntry = new ScOutlineEntry( nStartCol, nEndCol+1-nStartCol, bHidden ); + pNewEntry->SetVisible( bVisible ); + aCollections[nLevel].Insert( pNewEntry ); + + return TRUE; +} + +BOOL ScOutlineArray::FindTouchedLevel( SCCOLROW nBlockStart, SCCOLROW nBlockEnd, USHORT& rFindLevel ) const +{ + BOOL bFound = FALSE; + rFindLevel = 0; + + for (USHORT nLevel=0; nLevel<nDepth; nLevel++) + { + const ScOutlineCollection* pCollect = &aCollections[nLevel]; + USHORT nCount = pCollect->GetCount(); + for (USHORT i=0; i<nCount; i++) + { + ScOutlineEntry* pEntry = (ScOutlineEntry*) pCollect->At(i); + SCCOLROW nStart = pEntry->GetStart(); + SCCOLROW nEnd = pEntry->GetEnd(); + + if ( ( nBlockStart>=nStart && nBlockStart<=nEnd ) || + ( nBlockEnd >=nStart && nBlockEnd <=nEnd ) ) + { + rFindLevel = nLevel; // wirklicher Level + bFound = TRUE; + } + } + } + + return bFound; +} + +void ScOutlineArray::RemoveSub( SCCOLROW nStartPos, SCCOLROW nEndPos, USHORT nLevel ) +{ + if ( nLevel >= nDepth ) + return; + ScOutlineCollection* pCollect = &aCollections[nLevel]; + USHORT nCount = pCollect->GetCount(); + BOOL bFound = FALSE; + for ( USHORT i=0; i<nCount; i += ( bFound ? 0 : 1 ) ) + { + bFound = FALSE; + ScOutlineEntry* pEntry = (ScOutlineEntry*) pCollect->At(i); + SCCOLROW nStart = pEntry->GetStart(); + SCCOLROW nEnd = pEntry->GetEnd(); + + if ( nStart>=nStartPos && nEnd<=nEndPos ) + { + RemoveSub( nStart, nEnd, nLevel+1 ); + pCollect->AtFree(i); + nCount = pCollect->GetCount(); + bFound = TRUE; + } + } +} + +void ScOutlineArray::PromoteSub( SCCOLROW nStartPos, SCCOLROW nEndPos, USHORT nStartLevel ) +{ + if (nStartLevel==0) + { + DBG_ERROR("PromoteSub mit Level 0"); + return; + } + + for (USHORT nLevel = nStartLevel; nLevel < nDepth; nLevel++) + { + ScOutlineCollection* pCollect = &aCollections[nLevel]; + USHORT nCount = pCollect->GetCount(); + BOOL bFound = FALSE; + for ( USHORT i=0; i<nCount; i += ( bFound ? 0 : 1 ) ) + { + bFound = FALSE; + ScOutlineEntry* pEntry = (ScOutlineEntry*) pCollect->At(i); + SCCOLROW nStart = pEntry->GetStart(); + SCCOLROW nEnd = pEntry->GetEnd(); + + if ( nStart>=nStartPos && nEnd<=nEndPos ) + { + aCollections[nLevel-1].Insert( new ScOutlineEntry( *pEntry ) ); + pCollect->AtFree(i); + nCount = pCollect->GetCount(); + bFound = TRUE; + } + } + } +} + +BOOL ScOutlineArray::DecDepth() // nDepth auf leere Levels anpassen +{ + BOOL bChanged = FALSE; + BOOL bCont; + do + { + bCont = FALSE; + if (nDepth) + if (aCollections[nDepth-1].GetCount() == 0) + { + --nDepth; + bChanged = TRUE; + bCont = TRUE; + } + } + while (bCont); + return bChanged; +} + +BOOL ScOutlineArray::Remove( SCCOLROW nBlockStart, SCCOLROW nBlockEnd, BOOL& rSizeChanged ) +{ + USHORT nLevel; + FindTouchedLevel( nBlockStart, nBlockEnd, nLevel ); + + ScOutlineCollection* pCollect = &aCollections[nLevel]; + USHORT nCount = pCollect->GetCount(); + BOOL bFound = FALSE; + BOOL bAny = FALSE; + for ( USHORT i=0; i<nCount; i += ( bFound ? 0 : 1 ) ) + { + bFound = FALSE; + ScOutlineEntry* pEntry = (ScOutlineEntry*) pCollect->At(i); + SCCOLROW nStart = pEntry->GetStart(); + SCCOLROW nEnd = pEntry->GetEnd(); + + if ( nBlockStart<=nEnd && nBlockEnd>=nStart ) + { +// RemoveSub( nStart, nEnd, nLevel+1 ); + pCollect->AtFree(i); + PromoteSub( nStart, nEnd, nLevel+1 ); + nCount = pCollect->GetCount(); + i = pCollect->FindStart( nEnd+1 ); + bFound = TRUE; + bAny = TRUE; + } + } + + if (bAny) // Depth anpassen + if (DecDepth()) + rSizeChanged = TRUE; + + return bAny; +} + +ScOutlineEntry* ScOutlineArray::GetEntry( USHORT nLevel, USHORT nIndex ) const +{ + return (ScOutlineEntry*)((nLevel < nDepth) ? aCollections[nLevel].At(nIndex) : NULL); +} + +USHORT ScOutlineArray::GetCount( USHORT nLevel ) const +{ + return (nLevel < nDepth) ? aCollections[nLevel].GetCount() : 0; +} + +ScOutlineEntry* ScOutlineArray::GetEntryByPos( USHORT nLevel, SCCOLROW nPos ) const +{ + USHORT nCount = GetCount( nLevel ); + ScOutlineEntry* pEntry; + + for (USHORT nIndex = 0; nIndex < nCount; nIndex++) + { + pEntry = GetEntry( nLevel, nIndex ); + if ((pEntry->GetStart() <= nPos) && (nPos <= pEntry->GetEnd())) + return pEntry; + } + return NULL; +} + +BOOL ScOutlineArray::GetEntryIndex( USHORT nLevel, SCCOLROW nPos, USHORT& rnIndex ) const +{ + // found entry contains passed position + USHORT nCount = GetCount( nLevel ); + for ( rnIndex = 0; rnIndex < nCount; ++rnIndex ) + { + const ScOutlineEntry* pEntry = GetEntry( nLevel, rnIndex ); + if ( (pEntry->GetStart() <= nPos) && (nPos <= pEntry->GetEnd()) ) + return TRUE; + } + return FALSE; +} + +BOOL ScOutlineArray::GetEntryIndexInRange( + USHORT nLevel, SCCOLROW nBlockStart, SCCOLROW nBlockEnd, USHORT& rnIndex ) const +{ + // found entry will be completely inside of passed range + USHORT nCount = GetCount( nLevel ); + for ( rnIndex = 0; rnIndex < nCount; ++rnIndex ) + { + const ScOutlineEntry* pEntry = GetEntry( nLevel, rnIndex ); + if ( (nBlockStart <= pEntry->GetStart()) && (pEntry->GetEnd() <= nBlockEnd) ) + return TRUE; + } + return FALSE; +} + +void ScOutlineArray::SetVisibleBelow( USHORT nLevel, USHORT nEntry, BOOL bValue, BOOL bSkipHidden ) +{ + ScOutlineEntry* pEntry = GetEntry( nLevel, nEntry ); + if( pEntry ) + { + SCCOLROW nStart = pEntry->GetStart(); + SCCOLROW nEnd = pEntry->GetEnd(); + + for (USHORT nSubLevel=nLevel+1; nSubLevel<nDepth; nSubLevel++) + { + USHORT i = 0; + pEntry = (ScOutlineEntry*) aCollections[nSubLevel].At(i); + while (pEntry) + { + if (pEntry->GetStart() >= nStart && pEntry->GetEnd() <= nEnd) + { + pEntry->SetVisible(bValue); + + if (bSkipHidden) + if (!pEntry->IsHidden()) + SetVisibleBelow( nSubLevel, i, bValue, TRUE ); + } + + ++i; + pEntry = (ScOutlineEntry*) aCollections[nSubLevel].At(i); + } + + if (bSkipHidden) + nSubLevel = nDepth; // Abbruch + } + } +} + +void ScOutlineArray::GetRange( SCCOLROW& rStart, SCCOLROW& rEnd ) const +{ + USHORT nCount = aCollections[0].GetCount(); + if (nCount) + { + rStart = ((ScOutlineEntry*) aCollections[0].At(0))->GetStart(); + rEnd = ((ScOutlineEntry*) aCollections[0].At(nCount-1))->GetEnd(); + } + else + rStart = rEnd = 0; +} + +void ScOutlineArray::ExtendBlock( USHORT nLevel, SCCOLROW& rBlkStart, SCCOLROW& rBlkEnd ) +{ + USHORT nCount; + SCCOLROW nStart; + SCCOLROW nEnd; + USHORT i; + ScOutlineEntry* pEntry; + + nCount = GetCount(nLevel); + for ( i=0; i<nCount; i++ ) + { + pEntry = (ScOutlineEntry*) aCollections[nLevel].At(i); + nStart = pEntry->GetStart(); + nEnd = pEntry->GetEnd(); + + if ( rBlkStart<=nEnd && rBlkEnd>=nStart ) + { + if (nStart<rBlkStart) rBlkStart = nStart; + if (nEnd>rBlkEnd) rBlkEnd = nEnd; + } + } +} + +BOOL ScOutlineArray::TestInsertSpace( SCSIZE nSize, SCCOLROW nMaxVal ) const +{ + USHORT nCount = aCollections[0].GetCount(); + if (nCount) + { + SCCOLROW nEnd = ((ScOutlineEntry*) aCollections[0].At(nCount-1))->GetEnd(); + return ( sal::static_int_cast<SCCOLROW>(nEnd+nSize) <= nMaxVal ); + } + + return TRUE; +} + +void ScOutlineArray::InsertSpace( SCCOLROW nStartPos, SCSIZE nSize ) +{ + ScSubOutlineIterator aIter( this ); + ScOutlineEntry* pEntry; + while((pEntry=aIter.GetNext())!=NULL) + { + if ( pEntry->GetStart() >= nStartPos ) + pEntry->Move(static_cast<SCsCOLROW>(nSize)); + else + { + SCCOLROW nEnd = pEntry->GetEnd(); + // immer erweitern, wenn innerhalb der Gruppe eingefuegt + // beim Einfuegen am Ende nur, wenn die Gruppe nicht ausgeblendet ist + if ( nEnd >= nStartPos || ( nEnd+1 >= nStartPos && !pEntry->IsHidden() ) ) + { + SCSIZE nEntrySize = pEntry->GetSize(); + nEntrySize += nSize; + pEntry->SetSize( nEntrySize ); + } + } + } +} + +BOOL ScOutlineArray::DeleteSpace( SCCOLROW nStartPos, SCSIZE nSize ) +{ + SCCOLROW nEndPos = nStartPos + nSize - 1; + BOOL bNeedSave = FALSE; // Original fuer Undo benoetigt? + BOOL bChanged = FALSE; // fuer Test auf Level + + ScSubOutlineIterator aIter( this ); + ScOutlineEntry* pEntry; + while((pEntry=aIter.GetNext())!=NULL) + { + SCCOLROW nEntryStart = pEntry->GetStart(); + SCCOLROW nEntryEnd = pEntry->GetEnd(); + SCSIZE nEntrySize = pEntry->GetSize(); + + if ( nEntryEnd >= nStartPos ) + { + if ( nEntryStart > nEndPos ) // rechts + pEntry->Move(-(static_cast<SCsCOLROW>(nSize))); + else if ( nEntryStart < nStartPos && nEntryEnd >= nEndPos ) // aussen + pEntry->SetSize( nEntrySize-nSize ); + else + { + bNeedSave = TRUE; + if ( nEntryStart >= nStartPos && nEntryEnd <= nEndPos ) // innen + { + aIter.DeleteLast(); + bChanged = TRUE; + } + else if ( nEntryStart >= nStartPos ) // rechts ueber + pEntry->SetPosSize( nStartPos, static_cast<SCSIZE>(nEntryEnd-nEndPos) ); + else // links ueber + pEntry->SetSize( static_cast<SCSIZE>(nStartPos-nEntryStart) ); + } + } + } + + if (bChanged) + DecDepth(); + + return bNeedSave; +} + +BOOL ScOutlineArray::ManualAction( SCCOLROW nStartPos, SCCOLROW nEndPos, + BOOL bShow, const ScBitMaskCompressedArray< SCCOLROW, BYTE>& rHiddenFlags ) +{ + BOOL bModified = FALSE; + ScSubOutlineIterator aIter( this ); + ScOutlineEntry* pEntry; + while((pEntry=aIter.GetNext())!=NULL) + { + SCCOLROW nEntryStart = pEntry->GetStart(); + SCCOLROW nEntryEnd = pEntry->GetEnd(); + + if (nEntryEnd>=nStartPos && nEntryStart<=nEndPos) + { + if ( pEntry->IsHidden() == bShow ) + { + // #i12341# hide if all columns/rows are hidden, show if at least one + // is visible + + SCCOLROW nEnd = rHiddenFlags.GetBitStateEnd( nEntryStart, + CR_HIDDEN, CR_HIDDEN); + BOOL bAllHidden = (nEntryEnd <= nEnd && nEnd < + ::std::numeric_limits<SCCOLROW>::max()); + + BOOL bToggle = ( bShow != bAllHidden ); + if ( bToggle ) + { + pEntry->SetHidden( !bShow ); + SetVisibleBelow( aIter.LastLevel(), aIter.LastEntry(), bShow, bShow ); + bModified = TRUE; + } + } + } + } + return bModified; +} + +void ScOutlineArray::RemoveAll() +{ + for (USHORT nLevel=0; nLevel<nDepth; nLevel++) + aCollections[nLevel].FreeAll(); + + nDepth = 0; +} + +//------------------------------------------------------------------------ + +ScOutlineTable::ScOutlineTable() +{ +} + +ScOutlineTable::ScOutlineTable( const ScOutlineTable& rOutline ) : + aColOutline( rOutline.aColOutline ), + aRowOutline( rOutline.aRowOutline ) +{ +} + +BOOL ScOutlineTable::TestInsertCol( SCSIZE nSize ) +{ + return aColOutline.TestInsertSpace( nSize, MAXCOL ); +} + +void ScOutlineTable::InsertCol( SCCOL nStartCol, SCSIZE nSize ) +{ + aColOutline.InsertSpace( nStartCol, nSize ); +} + +BOOL ScOutlineTable::DeleteCol( SCCOL nStartCol, SCSIZE nSize ) +{ + return aColOutline.DeleteSpace( nStartCol, nSize ); +} + +BOOL ScOutlineTable::TestInsertRow( SCSIZE nSize ) +{ + return aRowOutline.TestInsertSpace( nSize, MAXROW ); +} + +void ScOutlineTable::InsertRow( SCROW nStartRow, SCSIZE nSize ) +{ + aRowOutline.InsertSpace( nStartRow, nSize ); +} + +BOOL ScOutlineTable::DeleteRow( SCROW nStartRow, SCSIZE nSize ) +{ + return aRowOutline.DeleteSpace( nStartRow, nSize ); +} + +//------------------------------------------------------------------------ + +ScSubOutlineIterator::ScSubOutlineIterator( ScOutlineArray* pOutlineArray ) : + pArray( pOutlineArray ), + nStart( 0 ), + nEnd( SCCOLROW_MAX ), // alle durchgehen + nSubLevel( 0 ), + nSubEntry( 0 ) +{ + nDepth = pArray->nDepth; +} + +ScSubOutlineIterator::ScSubOutlineIterator( ScOutlineArray* pOutlineArray, + USHORT nLevel, USHORT nEntry ) : + pArray( pOutlineArray ) +{ + ScOutlineEntry* pEntry = (ScOutlineEntry*) pArray->aCollections[nLevel].At(nEntry); + nStart = pEntry->GetStart(); + nEnd = pEntry->GetEnd(); + nSubLevel = nLevel + 1; + nSubEntry = 0; + nDepth = pArray->nDepth; +} + +ScOutlineEntry* ScSubOutlineIterator::GetNext() +{ + ScOutlineEntry* pEntry; + BOOL bFound = FALSE; + do + { + if (nSubLevel >= nDepth) + return NULL; + + pEntry = (ScOutlineEntry*) pArray->aCollections[nSubLevel].At(nSubEntry); + if (!pEntry) + { + nSubEntry = 0; + ++nSubLevel; + } + else + { + if ( pEntry->GetStart() >= nStart && pEntry->GetEnd() <= nEnd ) + bFound = TRUE; + ++nSubEntry; + } + } + while (!bFound); + return pEntry; // nSubLevel gueltig, wenn pEntry != 0 +} + +USHORT ScSubOutlineIterator::LastLevel() const +{ + return nSubLevel; +} + +USHORT ScSubOutlineIterator::LastEntry() const +{ + if (nSubEntry == 0) + { + DBG_ERROR("ScSubOutlineIterator::LastEntry vor GetNext"); + return 0; + } + return nSubEntry-1; +} + +void ScSubOutlineIterator::DeleteLast() +{ + if (nSubLevel >= nDepth) + { + DBG_ERROR("ScSubOutlineIterator::DeleteLast nach Ende"); + return; + } + if (nSubEntry == 0) + { + DBG_ERROR("ScSubOutlineIterator::DeleteLast vor GetNext"); + return; + } + + --nSubEntry; + pArray->aCollections[nSubLevel].AtFree(nSubEntry); +} + + diff --git a/sc/source/core/data/pagepar.cxx b/sc/source/core/data/pagepar.cxx new file mode 100644 index 000000000000..444f10a5df8e --- /dev/null +++ b/sc/source/core/data/pagepar.cxx @@ -0,0 +1,125 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pagepar.cxx,v $ + * $Revision: 1.7 $ + * + * 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 --------------------------------------------------------------- + +// System - Includes ----------------------------------------------------- + + + +#include <string.h> + +#include "pagepar.hxx" + + +//======================================================================== +// struct ScPageTableParam: + +ScPageTableParam::ScPageTableParam() +{ + Reset(); +} + +//------------------------------------------------------------------------ + +ScPageTableParam::~ScPageTableParam() +{ +} + +//------------------------------------------------------------------------ + +void ScPageTableParam::Reset() +{ + bCellContent = TRUE; + bNotes=bGrid=bHeaders=bDrawings= + bLeftRight=bScaleAll=bScaleTo=bScalePageNum= + bFormulas=bNullVals=bSkipEmpty = FALSE; + bTopDown=bScaleNone=bCharts=bObjects = TRUE; + nScaleAll = 100; + nScalePageNum = nScaleWidth = nScaleHeight = 0; + nFirstPageNo = 1; +} + +//------------------------------------------------------------------------ + +BOOL ScPageTableParam::operator==( const ScPageTableParam& r ) const +{ + return ( memcmp( this, &r, sizeof(ScPageTableParam) ) == 0 ); +} + +//======================================================================== +// struct ScPageAreaParam: + +ScPageAreaParam::ScPageAreaParam() +{ + Reset(); +} + +//------------------------------------------------------------------------ + +ScPageAreaParam::~ScPageAreaParam() +{ +} + +//------------------------------------------------------------------------ + +void ScPageAreaParam::Reset() +{ + bPrintArea = bRepeatRow = bRepeatCol = FALSE; + + memset( &aPrintArea, 0, sizeof(ScRange) ); + memset( &aRepeatRow, 0, sizeof(ScRange) ); + memset( &aRepeatCol, 0, sizeof(ScRange) ); +} + +//------------------------------------------------------------------------ + +BOOL ScPageAreaParam::operator==( const ScPageAreaParam& r ) const +{ + BOOL bEqual = + bPrintArea == r.bPrintArea + && bRepeatRow == r.bRepeatRow + && bRepeatCol == r.bRepeatCol; + + if ( bEqual ) + if ( bPrintArea ) + bEqual = bEqual && ( aPrintArea == r.aPrintArea ); + if ( bEqual ) + if ( bRepeatRow ) + bEqual = bEqual && ( aRepeatRow == r.aRepeatRow ); + if ( bEqual ) + if ( bRepeatCol ) + bEqual = bEqual && ( aRepeatCol == r.aRepeatCol ); + + return bEqual; +} diff --git a/sc/source/core/data/patattr.cxx b/sc/source/core/data/patattr.cxx new file mode 100644 index 000000000000..ee74a66bcc3b --- /dev/null +++ b/sc/source/core/data/patattr.cxx @@ -0,0 +1,1350 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: patattr.cxx,v $ + * $Revision: 1.34.144.1 $ + * + * 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 "scitems.hxx" +#include <svx/adjitem.hxx> +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/bolnitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/charreliefitem.hxx> +#include <svx/cntritem.hxx> +#include <svtools/colorcfg.hxx> +#include <svx/colritem.hxx> +#include <svx/crsditem.hxx> +#include <svx/emphitem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/fontitem.hxx> +#include <svx/forbiddenruleitem.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/langitem.hxx> +#include <svx/postitem.hxx> +#include <svx/rotmodit.hxx> +#include <svx/scriptspaceitem.hxx> +#include <svx/scripttypeitem.hxx> +#include <svx/shaditem.hxx> +#include <svx/shdditem.hxx> +#include <svx/udlnitem.hxx> +#include <svx/wghtitem.hxx> +#include <svx/wrlmitem.hxx> +#include <svtools/intitem.hxx> +#include <svtools/zforlist.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + +#include "patattr.hxx" +#include "docpool.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" +#include "document.hxx" +#include "global.hxx" +#include "globstr.hrc" +#include "conditio.hxx" +#include "validat.hxx" +#include "scmod.hxx" +#include "fillinfo.hxx" + +// STATIC DATA ----------------------------------------------------------- + +ScDocument* ScPatternAttr::pDoc = NULL; + +// ----------------------------------------------------------------------- + +//! move to some header file +inline long TwipsToHMM(long nTwips) { return (nTwips * 127 + 36) / 72; } +inline long HMMToTwips(long nHMM) { return (nHMM * 72 + 63) / 127; } + +// ----------------------------------------------------------------------- + +ScPatternAttr::ScPatternAttr( SfxItemSet* pItemSet, const String& rStyleName ) + : SfxSetItem ( ATTR_PATTERN, pItemSet ), + pName ( new String( rStyleName ) ), + pStyle ( NULL ) +{ +} + +ScPatternAttr::ScPatternAttr( SfxItemSet* pItemSet, ScStyleSheet* pStyleSheet ) + : SfxSetItem ( ATTR_PATTERN, pItemSet ), + pName ( NULL ), + pStyle ( pStyleSheet ) +{ + if ( pStyleSheet ) + GetItemSet().SetParent( &pStyleSheet->GetItemSet() ); +} + +ScPatternAttr::ScPatternAttr( SfxItemPool* pItemPool ) + : SfxSetItem ( ATTR_PATTERN, new SfxItemSet( *pItemPool, ATTR_PATTERN_START, ATTR_PATTERN_END ) ), + pName ( NULL ), + pStyle ( NULL ) +{ +} + +ScPatternAttr::ScPatternAttr( const ScPatternAttr& rPatternAttr ) + : SfxSetItem ( rPatternAttr ), + pStyle ( rPatternAttr.pStyle ) +{ + if (rPatternAttr.pName) + pName = new String(*rPatternAttr.pName); + else + pName = NULL; +} + +__EXPORT ScPatternAttr::~ScPatternAttr() +{ + delete pName; +} + +SfxPoolItem* __EXPORT ScPatternAttr::Clone( SfxItemPool *pPool ) const +{ + ScPatternAttr* pPattern = new ScPatternAttr( GetItemSet().Clone(TRUE, pPool) ); + + pPattern->pStyle = pStyle; + pPattern->pName = pName ? new String(*pName) : NULL; + + return pPattern; +} + +inline int StrCmp( const String* pStr1, const String* pStr2 ) +{ + return ( pStr1 ? ( pStr2 ? ( *pStr1 == *pStr2 ) : FALSE ) : ( pStr2 ? FALSE : TRUE ) ); +} + +inline bool EqualPatternSets( const SfxItemSet& rSet1, const SfxItemSet& rSet2 ) +{ + // #i62090# The SfxItemSet in the SfxSetItem base class always has the same ranges + // (single range from ATTR_PATTERN_START to ATTR_PATTERN_END), and the items are pooled, + // so it's enough to compare just the pointers (Count just because it's even faster). + + if ( rSet1.Count() != rSet2.Count() ) + return false; + + SfxItemArray pItems1 = rSet1.GetItems_Impl(); // inline method of SfxItemSet + SfxItemArray pItems2 = rSet2.GetItems_Impl(); + + return ( 0 == memcmp( pItems1, pItems2, (ATTR_PATTERN_END - ATTR_PATTERN_START + 1) * sizeof(pItems1[0]) ) ); +} + +int __EXPORT ScPatternAttr::operator==( const SfxPoolItem& rCmp ) const +{ + // #i62090# Use quick comparison between ScPatternAttr's ItemSets + + return ( EqualPatternSets( GetItemSet(), static_cast<const ScPatternAttr&>(rCmp).GetItemSet() ) && + StrCmp( GetStyleName(), static_cast<const ScPatternAttr&>(rCmp).GetStyleName() ) ); +} + +SfxPoolItem* __EXPORT ScPatternAttr::Create( SvStream& rStream, USHORT /* nVersion */ ) const +{ + String* pStr; + BOOL bHasStyle; + short eFamDummy; + + rStream >> bHasStyle; + + if ( bHasStyle ) + { + pStr = new String; + rStream.ReadByteString( *pStr, rStream.GetStreamCharSet() ); + rStream >> eFamDummy; // wg. altem Dateiformat + } + else + pStr = new String( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ); + + SfxItemSet *pNewSet = new SfxItemSet( *GetItemSet().GetPool(), + ATTR_PATTERN_START, ATTR_PATTERN_END ); + pNewSet->Load( rStream ); + + ScPatternAttr* pPattern = new ScPatternAttr( pNewSet ); + + pPattern->pName = pStr; + + return pPattern; +} + +SvStream& __EXPORT ScPatternAttr::Store(SvStream& rStream, USHORT /* nItemVersion */) const +{ + rStream << (BOOL)TRUE; + + if ( pStyle ) + rStream.WriteByteString( pStyle->GetName(), rStream.GetStreamCharSet() ); + else if ( pName ) // wenn Style geloescht ist/war + rStream.WriteByteString( *pName, rStream.GetStreamCharSet() ); + else + rStream.WriteByteString( ScGlobal::GetRscString(STR_STYLENAME_STANDARD), + rStream.GetStreamCharSet() ); + + rStream << (short)SFX_STYLE_FAMILY_PARA; // wg. altem Dateiformat + + GetItemSet().Store( rStream ); + + return rStream; +} + +SvxCellOrientation ScPatternAttr::GetCellOrientation( const SfxItemSet& rItemSet, const SfxItemSet* pCondSet ) +{ + SvxCellOrientation eOrient = SVX_ORIENTATION_STANDARD; + + if( ((const SfxBoolItem&)GetItem( ATTR_STACKED, rItemSet, pCondSet )).GetValue() ) + { + eOrient = SVX_ORIENTATION_STACKED; + } + else + { + INT32 nAngle = ((const SfxInt32Item&)GetItem( ATTR_ROTATE_VALUE, rItemSet, pCondSet )).GetValue(); + if( nAngle == 9000 ) + eOrient = SVX_ORIENTATION_BOTTOMTOP; + else if( nAngle == 27000 ) + eOrient = SVX_ORIENTATION_TOPBOTTOM; + } + + return eOrient; +} + +SvxCellOrientation ScPatternAttr::GetCellOrientation( const SfxItemSet* pCondSet ) const +{ + return GetCellOrientation( GetItemSet(), pCondSet ); +} + +void ScPatternAttr::GetFont( + Font& rFont, const SfxItemSet& rItemSet, ScAutoFontColorMode eAutoMode, + OutputDevice* pOutDev, const Fraction* pScale, + const SfxItemSet* pCondSet, BYTE nScript, + const Color* pBackConfigColor, const Color* pTextConfigColor ) +{ + // Items auslesen + + const SvxFontItem* pFontAttr; + UINT32 nFontHeight; + FontWeight eWeight; + FontItalic eItalic; + FontUnderline eUnder; + FontUnderline eOver; + BOOL bWordLine; + FontStrikeout eStrike; + BOOL bOutline; + BOOL bShadow; + FontEmphasisMark eEmphasis; + FontRelief eRelief; + Color aColor; + LanguageType eLang; + + USHORT nFontId, nHeightId, nWeightId, nPostureId, nLangId; + if ( nScript == SCRIPTTYPE_ASIAN ) + { + nFontId = ATTR_CJK_FONT; + nHeightId = ATTR_CJK_FONT_HEIGHT; + nWeightId = ATTR_CJK_FONT_WEIGHT; + nPostureId = ATTR_CJK_FONT_POSTURE; + nLangId = ATTR_CJK_FONT_LANGUAGE; + } + else if ( nScript == SCRIPTTYPE_COMPLEX ) + { + nFontId = ATTR_CTL_FONT; + nHeightId = ATTR_CTL_FONT_HEIGHT; + nWeightId = ATTR_CTL_FONT_WEIGHT; + nPostureId = ATTR_CTL_FONT_POSTURE; + nLangId = ATTR_CTL_FONT_LANGUAGE; + } + else + { + nFontId = ATTR_FONT; + nHeightId = ATTR_FONT_HEIGHT; + nWeightId = ATTR_FONT_WEIGHT; + nPostureId = ATTR_FONT_POSTURE; + nLangId = ATTR_FONT_LANGUAGE; + } + + if ( pCondSet ) + { + const SfxPoolItem* pItem; + + if ( pCondSet->GetItemState( nFontId, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( nFontId ); + pFontAttr = (const SvxFontItem*) pItem; + + if ( pCondSet->GetItemState( nHeightId, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( nHeightId ); + nFontHeight = ((const SvxFontHeightItem*)pItem)->GetHeight(); + + if ( pCondSet->GetItemState( nWeightId, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( nWeightId ); + eWeight = (FontWeight)((const SvxWeightItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( nPostureId, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( nPostureId ); + eItalic = (FontItalic)((const SvxPostureItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_UNDERLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_UNDERLINE ); + eUnder = (FontUnderline)((const SvxUnderlineItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_OVERLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_OVERLINE ); + eOver = (FontUnderline)((const SvxOverlineItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_WORDLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_WORDLINE ); + bWordLine = ((const SvxWordLineModeItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_CROSSEDOUT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_CROSSEDOUT ); + eStrike = (FontStrikeout)((const SvxCrossedOutItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_CONTOUR, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_CONTOUR ); + bOutline = ((const SvxContourItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_SHADOWED, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_SHADOWED ); + bShadow = ((const SvxShadowedItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_EMPHASISMARK, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_EMPHASISMARK ); + eEmphasis = ((const SvxEmphasisMarkItem*)pItem)->GetEmphasisMark(); + + if ( pCondSet->GetItemState( ATTR_FONT_RELIEF, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_RELIEF ); + eRelief = (FontRelief)((const SvxCharReliefItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_COLOR, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_FONT_COLOR ); + aColor = ((const SvxColorItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( nLangId, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( nLangId ); + eLang = ((const SvxLanguageItem*)pItem)->GetLanguage(); + } + else // alles aus rItemSet + { + pFontAttr = &(const SvxFontItem&)rItemSet.Get( nFontId ); + nFontHeight = ((const SvxFontHeightItem&) + rItemSet.Get( nHeightId )).GetHeight(); + eWeight = (FontWeight)((const SvxWeightItem&) + rItemSet.Get( nWeightId )).GetValue(); + eItalic = (FontItalic)((const SvxPostureItem&) + rItemSet.Get( nPostureId )).GetValue(); + eUnder = (FontUnderline)((const SvxUnderlineItem&) + rItemSet.Get( ATTR_FONT_UNDERLINE )).GetValue(); + eOver = (FontUnderline)((const SvxOverlineItem&) + rItemSet.Get( ATTR_FONT_OVERLINE )).GetValue(); + bWordLine = ((const SvxWordLineModeItem&) + rItemSet.Get( ATTR_FONT_WORDLINE )).GetValue(); + eStrike = (FontStrikeout)((const SvxCrossedOutItem&) + rItemSet.Get( ATTR_FONT_CROSSEDOUT )).GetValue(); + bOutline = ((const SvxContourItem&) + rItemSet.Get( ATTR_FONT_CONTOUR )).GetValue(); + bShadow = ((const SvxShadowedItem&) + rItemSet.Get( ATTR_FONT_SHADOWED )).GetValue(); + eEmphasis = ((const SvxEmphasisMarkItem&) + rItemSet.Get( ATTR_FONT_EMPHASISMARK )).GetEmphasisMark(); + eRelief = (FontRelief)((const SvxCharReliefItem&) + rItemSet.Get( ATTR_FONT_RELIEF )).GetValue(); + aColor = ((const SvxColorItem&) + rItemSet.Get( ATTR_FONT_COLOR )).GetValue(); + // for graphite language features + eLang = + ((const SvxLanguageItem&)rItemSet.Get( nLangId )).GetLanguage(); + } + DBG_ASSERT(pFontAttr,"nanu?"); + + // auswerten + + // FontItem: + + if (rFont.GetName() != pFontAttr->GetFamilyName()) + rFont.SetName( pFontAttr->GetFamilyName() ); + if (rFont.GetStyleName() != pFontAttr->GetStyleName()) + rFont.SetStyleName( pFontAttr->GetStyleName() ); + + rFont.SetFamily( pFontAttr->GetFamily() ); + rFont.SetCharSet( pFontAttr->GetCharSet() ); + rFont.SetPitch( pFontAttr->GetPitch() ); + + rFont.SetLanguage(eLang); + + // Groesse + + if ( pOutDev != NULL ) + { + Size aEffSize; + Fraction aFraction( 1,1 ); + if (pScale) + aFraction = *pScale; + Size aSize( 0, (long) nFontHeight ); + MapMode aDestMode = pOutDev->GetMapMode(); + MapMode aSrcMode( MAP_TWIP, Point(), aFraction, aFraction ); + if (aDestMode.GetMapUnit() == MAP_PIXEL) + aEffSize = pOutDev->LogicToPixel( aSize, aSrcMode ); + else + { + Fraction aFractOne(1,1); + aDestMode.SetScaleX( aFractOne ); + aDestMode.SetScaleY( aFractOne ); + aEffSize = OutputDevice::LogicToLogic( aSize, aSrcMode, aDestMode ); + } + rFont.SetSize( aEffSize ); + } + else /* if pOutDev != NULL */ + { + rFont.SetSize( Size( 0, (long) nFontHeight ) ); + } + + // determine effective font color + + if ( ( aColor.GetColor() == COL_AUTO && eAutoMode != SC_AUTOCOL_RAW ) || + eAutoMode == SC_AUTOCOL_IGNOREFONT || eAutoMode == SC_AUTOCOL_IGNOREALL ) + { + if ( eAutoMode == SC_AUTOCOL_BLACK ) + aColor.SetColor( COL_BLACK ); + else + { + // get background color from conditional or own set + Color aBackColor; + if ( pCondSet ) + { + const SfxPoolItem* pItem; + if ( pCondSet->GetItemState( ATTR_BACKGROUND, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rItemSet.Get( ATTR_BACKGROUND ); + aBackColor = ((const SvxBrushItem*)pItem)->GetColor(); + } + else + aBackColor = ((const SvxBrushItem&)rItemSet.Get( ATTR_BACKGROUND )).GetColor(); + + // if background color attribute is transparent, use window color for brightness comparisons + if ( aBackColor == COL_TRANSPARENT || + eAutoMode == SC_AUTOCOL_IGNOREBACK || eAutoMode == SC_AUTOCOL_IGNOREALL ) + { + if ( eAutoMode == SC_AUTOCOL_PRINT ) + aBackColor.SetColor( COL_WHITE ); + else if ( pBackConfigColor ) + { + // pBackConfigColor can be used to avoid repeated lookup of the configured color + aBackColor = *pBackConfigColor; + } + else + aBackColor.SetColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::DOCCOLOR).nColor ); + } + + // get system text color for comparison + Color aSysTextColor; + if ( eAutoMode == SC_AUTOCOL_PRINT ) + aSysTextColor.SetColor( COL_BLACK ); + else if ( pTextConfigColor ) + { + // pTextConfigColor can be used to avoid repeated lookup of the configured color + aSysTextColor = *pTextConfigColor; + } + else + aSysTextColor.SetColor( SC_MOD()->GetColorConfig().GetColorValue(svtools::FONTCOLOR).nColor ); + + // select the resulting color + if ( aBackColor.IsDark() && aSysTextColor.IsDark() ) + { + // use white instead of dark on dark + aColor.SetColor( COL_WHITE ); + } + else if ( aBackColor.IsBright() && aSysTextColor.IsBright() ) + { + // use black instead of bright on bright + aColor.SetColor( COL_BLACK ); + } + else + { + // use aSysTextColor (black for SC_AUTOCOL_PRINT, from style settings otherwise) + aColor = aSysTextColor; + } + } + } + + // set font effects + rFont.SetWeight( eWeight ); + rFont.SetItalic( eItalic ); + rFont.SetUnderline( eUnder ); + rFont.SetOverline( eOver ); + rFont.SetWordLineMode( bWordLine ); + rFont.SetStrikeout( eStrike ); + rFont.SetOutline( bOutline ); + rFont.SetShadow( bShadow ); + rFont.SetEmphasisMark( eEmphasis ); + rFont.SetRelief( eRelief ); + rFont.SetColor( aColor ); + rFont.SetTransparent( TRUE ); +} + +void ScPatternAttr::GetFont( + Font& rFont, ScAutoFontColorMode eAutoMode, + OutputDevice* pOutDev, const Fraction* pScale, + const SfxItemSet* pCondSet, BYTE nScript, + const Color* pBackConfigColor, const Color* pTextConfigColor ) const +{ + GetFont( rFont, GetItemSet(), eAutoMode, pOutDev, pScale, pCondSet, nScript, pBackConfigColor, pTextConfigColor ); +} + + +void ScPatternAttr::FillToEditItemSet( SfxItemSet& rEditSet, const SfxItemSet& rSrcSet, const SfxItemSet* pCondSet ) +{ + // Items auslesen + + SvxColorItem aColorItem(EE_CHAR_COLOR); // use item as-is + SvxFontItem aFontItem(EE_CHAR_FONTINFO); // use item as-is + SvxFontItem aCjkFontItem(EE_CHAR_FONTINFO_CJK); + SvxFontItem aCtlFontItem(EE_CHAR_FONTINFO_CTL); + long nTHeight, nCjkTHeight, nCtlTHeight; // Twips + FontWeight eWeight, eCjkWeight, eCtlWeight; + SvxUnderlineItem aUnderlineItem(UNDERLINE_NONE, EE_CHAR_UNDERLINE); + SvxOverlineItem aOverlineItem(UNDERLINE_NONE, EE_CHAR_OVERLINE); + BOOL bWordLine; + FontStrikeout eStrike; + FontItalic eItalic, eCjkItalic, eCtlItalic; + BOOL bOutline; + BOOL bShadow; + BOOL bForbidden; + FontEmphasisMark eEmphasis; + FontRelief eRelief; + LanguageType eLang, eCjkLang, eCtlLang; + BOOL bHyphenate; + SvxFrameDirection eDirection; + + //! additional parameter to control if language is needed? + + if ( pCondSet ) + { + const SfxPoolItem* pItem; + + if ( pCondSet->GetItemState( ATTR_FONT_COLOR, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_COLOR ); + aColorItem = *(const SvxColorItem*)pItem; + + if ( pCondSet->GetItemState( ATTR_FONT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT ); + aFontItem = *(const SvxFontItem*)pItem; + if ( pCondSet->GetItemState( ATTR_CJK_FONT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CJK_FONT ); + aCjkFontItem = *(const SvxFontItem*)pItem; + if ( pCondSet->GetItemState( ATTR_CTL_FONT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CTL_FONT ); + aCtlFontItem = *(const SvxFontItem*)pItem; + + if ( pCondSet->GetItemState( ATTR_FONT_HEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_HEIGHT ); + nTHeight = ((const SvxFontHeightItem*)pItem)->GetHeight(); + if ( pCondSet->GetItemState( ATTR_CJK_FONT_HEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CJK_FONT_HEIGHT ); + nCjkTHeight = ((const SvxFontHeightItem*)pItem)->GetHeight(); + if ( pCondSet->GetItemState( ATTR_CTL_FONT_HEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CTL_FONT_HEIGHT ); + nCtlTHeight = ((const SvxFontHeightItem*)pItem)->GetHeight(); + + if ( pCondSet->GetItemState( ATTR_FONT_WEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_WEIGHT ); + eWeight = (FontWeight)((const SvxWeightItem*)pItem)->GetValue(); + if ( pCondSet->GetItemState( ATTR_CJK_FONT_WEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CJK_FONT_WEIGHT ); + eCjkWeight = (FontWeight)((const SvxWeightItem*)pItem)->GetValue(); + if ( pCondSet->GetItemState( ATTR_CTL_FONT_WEIGHT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CTL_FONT_WEIGHT ); + eCtlWeight = (FontWeight)((const SvxWeightItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_POSTURE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_POSTURE ); + eItalic = (FontItalic)((const SvxPostureItem*)pItem)->GetValue(); + if ( pCondSet->GetItemState( ATTR_CJK_FONT_POSTURE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CJK_FONT_POSTURE ); + eCjkItalic = (FontItalic)((const SvxPostureItem*)pItem)->GetValue(); + if ( pCondSet->GetItemState( ATTR_CTL_FONT_POSTURE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CTL_FONT_POSTURE ); + eCtlItalic = (FontItalic)((const SvxPostureItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_UNDERLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_UNDERLINE ); + aUnderlineItem = *(const SvxUnderlineItem*)pItem; + + if ( pCondSet->GetItemState( ATTR_FONT_OVERLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_OVERLINE ); + aOverlineItem = *(const SvxOverlineItem*)pItem; + + if ( pCondSet->GetItemState( ATTR_FONT_WORDLINE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_WORDLINE ); + bWordLine = ((const SvxWordLineModeItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_CROSSEDOUT, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_CROSSEDOUT ); + eStrike = (FontStrikeout)((const SvxCrossedOutItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_CONTOUR, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_CONTOUR ); + bOutline = ((const SvxContourItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_SHADOWED, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_SHADOWED ); + bShadow = ((const SvxShadowedItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FORBIDDEN_RULES, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FORBIDDEN_RULES ); + bForbidden = ((const SvxForbiddenRuleItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_EMPHASISMARK, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_EMPHASISMARK ); + eEmphasis = ((const SvxEmphasisMarkItem*)pItem)->GetEmphasisMark(); + if ( pCondSet->GetItemState( ATTR_FONT_RELIEF, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_RELIEF ); + eRelief = (FontRelief)((const SvxCharReliefItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_FONT_LANGUAGE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_FONT_LANGUAGE ); + eLang = ((const SvxLanguageItem*)pItem)->GetLanguage(); + if ( pCondSet->GetItemState( ATTR_CJK_FONT_LANGUAGE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CJK_FONT_LANGUAGE ); + eCjkLang = ((const SvxLanguageItem*)pItem)->GetLanguage(); + if ( pCondSet->GetItemState( ATTR_CTL_FONT_LANGUAGE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_CTL_FONT_LANGUAGE ); + eCtlLang = ((const SvxLanguageItem*)pItem)->GetLanguage(); + + if ( pCondSet->GetItemState( ATTR_HYPHENATE, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_HYPHENATE ); + bHyphenate = ((const SfxBoolItem*)pItem)->GetValue(); + + if ( pCondSet->GetItemState( ATTR_WRITINGDIR, TRUE, &pItem ) != SFX_ITEM_SET ) + pItem = &rSrcSet.Get( ATTR_WRITINGDIR ); + eDirection = (SvxFrameDirection)((const SvxFrameDirectionItem*)pItem)->GetValue(); + } + else // alles direkt aus Pattern + { + aColorItem = (const SvxColorItem&) rSrcSet.Get( ATTR_FONT_COLOR ); + aFontItem = (const SvxFontItem&) rSrcSet.Get( ATTR_FONT ); + aCjkFontItem = (const SvxFontItem&) rSrcSet.Get( ATTR_CJK_FONT ); + aCtlFontItem = (const SvxFontItem&) rSrcSet.Get( ATTR_CTL_FONT ); + nTHeight = ((const SvxFontHeightItem&) + rSrcSet.Get( ATTR_FONT_HEIGHT )).GetHeight(); + nCjkTHeight = ((const SvxFontHeightItem&) + rSrcSet.Get( ATTR_CJK_FONT_HEIGHT )).GetHeight(); + nCtlTHeight = ((const SvxFontHeightItem&) + rSrcSet.Get( ATTR_CTL_FONT_HEIGHT )).GetHeight(); + eWeight = (FontWeight)((const SvxWeightItem&) + rSrcSet.Get( ATTR_FONT_WEIGHT )).GetValue(); + eCjkWeight = (FontWeight)((const SvxWeightItem&) + rSrcSet.Get( ATTR_CJK_FONT_WEIGHT )).GetValue(); + eCtlWeight = (FontWeight)((const SvxWeightItem&) + rSrcSet.Get( ATTR_CTL_FONT_WEIGHT )).GetValue(); + eItalic = (FontItalic)((const SvxPostureItem&) + rSrcSet.Get( ATTR_FONT_POSTURE )).GetValue(); + eCjkItalic = (FontItalic)((const SvxPostureItem&) + rSrcSet.Get( ATTR_CJK_FONT_POSTURE )).GetValue(); + eCtlItalic = (FontItalic)((const SvxPostureItem&) + rSrcSet.Get( ATTR_CTL_FONT_POSTURE )).GetValue(); + aUnderlineItem = (const SvxUnderlineItem&) rSrcSet.Get( ATTR_FONT_UNDERLINE ); + aOverlineItem = (const SvxOverlineItem&) rSrcSet.Get( ATTR_FONT_OVERLINE ); + bWordLine = ((const SvxWordLineModeItem&) + rSrcSet.Get( ATTR_FONT_WORDLINE )).GetValue(); + eStrike = (FontStrikeout)((const SvxCrossedOutItem&) + rSrcSet.Get( ATTR_FONT_CROSSEDOUT )).GetValue(); + bOutline = ((const SvxContourItem&) + rSrcSet.Get( ATTR_FONT_CONTOUR )).GetValue(); + bShadow = ((const SvxShadowedItem&) + rSrcSet.Get( ATTR_FONT_SHADOWED )).GetValue(); + bForbidden = ((const SvxForbiddenRuleItem&) + rSrcSet.Get( ATTR_FORBIDDEN_RULES )).GetValue(); + eEmphasis = ((const SvxEmphasisMarkItem&) + rSrcSet.Get( ATTR_FONT_EMPHASISMARK )).GetEmphasisMark(); + eRelief = (FontRelief)((const SvxCharReliefItem&) + rSrcSet.Get( ATTR_FONT_RELIEF )).GetValue(); + eLang = ((const SvxLanguageItem&) + rSrcSet.Get( ATTR_FONT_LANGUAGE )).GetLanguage(); + eCjkLang = ((const SvxLanguageItem&) + rSrcSet.Get( ATTR_CJK_FONT_LANGUAGE )).GetLanguage(); + eCtlLang = ((const SvxLanguageItem&) + rSrcSet.Get( ATTR_CTL_FONT_LANGUAGE )).GetLanguage(); + bHyphenate = ((const SfxBoolItem&) + rSrcSet.Get( ATTR_HYPHENATE )).GetValue(); + eDirection = (SvxFrameDirection)((const SvxFrameDirectionItem&) + rSrcSet.Get( ATTR_WRITINGDIR )).GetValue(); + } + + // kompatibel zu LogicToLogic rechnen, also 2540/1440 = 127/72, und runden + + long nHeight = TwipsToHMM(nTHeight); + long nCjkHeight = TwipsToHMM(nCjkTHeight); + long nCtlHeight = TwipsToHMM(nCtlTHeight); + + // put items into EditEngine ItemSet + + if ( aColorItem.GetValue().GetColor() == COL_AUTO ) + { + // #108979# When cell attributes are converted to EditEngine paragraph attributes, + // don't create a hard item for automatic color, because that would be converted + // to black when the item's Store method is used in CreateTransferable/WriteBin. + // COL_AUTO is the EditEngine's pool default, so ClearItem will result in automatic + // color, too, without having to store the item. + rEditSet.ClearItem( EE_CHAR_COLOR ); + } + else + rEditSet.Put( aColorItem ); + rEditSet.Put( aFontItem ); + rEditSet.Put( aCjkFontItem ); + rEditSet.Put( aCtlFontItem ); + rEditSet.Put( SvxFontHeightItem( nHeight, 100, EE_CHAR_FONTHEIGHT ) ); + rEditSet.Put( SvxFontHeightItem( nCjkHeight, 100, EE_CHAR_FONTHEIGHT_CJK ) ); + rEditSet.Put( SvxFontHeightItem( nCtlHeight, 100, EE_CHAR_FONTHEIGHT_CTL ) ); + rEditSet.Put( SvxWeightItem ( eWeight, EE_CHAR_WEIGHT ) ); + rEditSet.Put( SvxWeightItem ( eCjkWeight, EE_CHAR_WEIGHT_CJK ) ); + rEditSet.Put( SvxWeightItem ( eCtlWeight, EE_CHAR_WEIGHT_CTL ) ); + rEditSet.Put( aUnderlineItem ); + rEditSet.Put( aOverlineItem ); + rEditSet.Put( SvxWordLineModeItem( bWordLine, EE_CHAR_WLM ) ); + rEditSet.Put( SvxCrossedOutItem( eStrike, EE_CHAR_STRIKEOUT ) ); + rEditSet.Put( SvxPostureItem ( eItalic, EE_CHAR_ITALIC ) ); + rEditSet.Put( SvxPostureItem ( eCjkItalic, EE_CHAR_ITALIC_CJK ) ); + rEditSet.Put( SvxPostureItem ( eCtlItalic, EE_CHAR_ITALIC_CTL ) ); + rEditSet.Put( SvxContourItem ( bOutline, EE_CHAR_OUTLINE ) ); + rEditSet.Put( SvxShadowedItem ( bShadow, EE_CHAR_SHADOW ) ); + rEditSet.Put( SfxBoolItem ( EE_PARA_FORBIDDENRULES, bForbidden ) ); + rEditSet.Put( SvxEmphasisMarkItem( eEmphasis, EE_CHAR_EMPHASISMARK ) ); + rEditSet.Put( SvxCharReliefItem( eRelief, EE_CHAR_RELIEF ) ); + rEditSet.Put( SvxLanguageItem ( eLang, EE_CHAR_LANGUAGE ) ); + rEditSet.Put( SvxLanguageItem ( eCjkLang, EE_CHAR_LANGUAGE_CJK ) ); + rEditSet.Put( SvxLanguageItem ( eCtlLang, EE_CHAR_LANGUAGE_CTL ) ); + rEditSet.Put( SfxBoolItem ( EE_PARA_HYPHENATE, bHyphenate ) ); + rEditSet.Put( SvxFrameDirectionItem( eDirection, EE_PARA_WRITINGDIR ) ); + + // #111216# Script spacing is always off. + // The cell attribute isn't used here as long as there is no UI to set it + // (don't evaluate attributes that can't be changed). + // If a locale-dependent default is needed, it has to go into the cell + // style, like the fonts. + rEditSet.Put( SvxScriptSpaceItem( FALSE, EE_PARA_ASIANCJKSPACING ) ); +} + +void ScPatternAttr::FillEditItemSet( SfxItemSet* pEditSet, const SfxItemSet* pCondSet ) const +{ + if( pEditSet ) + FillToEditItemSet( *pEditSet, GetItemSet(), pCondSet ); +} + + +void ScPatternAttr::GetFromEditItemSet( SfxItemSet& rDestSet, const SfxItemSet& rEditSet ) +{ + const SfxPoolItem* pItem; + + if (rEditSet.GetItemState(EE_CHAR_COLOR,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxColorItem(ATTR_FONT_COLOR) = *(const SvxColorItem*)pItem ); + + if (rEditSet.GetItemState(EE_CHAR_FONTINFO,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontItem(ATTR_FONT) = *(const SvxFontItem*)pItem ); + if (rEditSet.GetItemState(EE_CHAR_FONTINFO_CJK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontItem(ATTR_CJK_FONT) = *(const SvxFontItem*)pItem ); + if (rEditSet.GetItemState(EE_CHAR_FONTINFO_CTL,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontItem(ATTR_CTL_FONT) = *(const SvxFontItem*)pItem ); + + if (rEditSet.GetItemState(EE_CHAR_FONTHEIGHT,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontHeightItem( HMMToTwips( ((const SvxFontHeightItem*)pItem)->GetHeight() ), + 100, ATTR_FONT_HEIGHT ) ); + if (rEditSet.GetItemState(EE_CHAR_FONTHEIGHT_CJK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontHeightItem( HMMToTwips( ((const SvxFontHeightItem*)pItem)->GetHeight() ), + 100, ATTR_CJK_FONT_HEIGHT ) ); + if (rEditSet.GetItemState(EE_CHAR_FONTHEIGHT_CTL,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxFontHeightItem( HMMToTwips( ((const SvxFontHeightItem*)pItem)->GetHeight() ), + 100, ATTR_CTL_FONT_HEIGHT ) ); + + if (rEditSet.GetItemState(EE_CHAR_WEIGHT,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxWeightItem( (FontWeight)((const SvxWeightItem*)pItem)->GetValue(), + ATTR_FONT_WEIGHT) ); + if (rEditSet.GetItemState(EE_CHAR_WEIGHT_CJK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxWeightItem( (FontWeight)((const SvxWeightItem*)pItem)->GetValue(), + ATTR_CJK_FONT_WEIGHT) ); + if (rEditSet.GetItemState(EE_CHAR_WEIGHT_CTL,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxWeightItem( (FontWeight)((const SvxWeightItem*)pItem)->GetValue(), + ATTR_CTL_FONT_WEIGHT) ); + + // SvxTextLineItem contains enum and color + if (rEditSet.GetItemState(EE_CHAR_UNDERLINE,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxUnderlineItem(UNDERLINE_NONE,ATTR_FONT_UNDERLINE) = *(const SvxUnderlineItem*)pItem ); + if (rEditSet.GetItemState(EE_CHAR_OVERLINE,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxOverlineItem(UNDERLINE_NONE,ATTR_FONT_OVERLINE) = *(const SvxOverlineItem*)pItem ); + if (rEditSet.GetItemState(EE_CHAR_WLM,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxWordLineModeItem( ((const SvxWordLineModeItem*)pItem)->GetValue(), + ATTR_FONT_WORDLINE) ); + + if (rEditSet.GetItemState(EE_CHAR_STRIKEOUT,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxCrossedOutItem( (FontStrikeout)((const SvxCrossedOutItem*)pItem)->GetValue(), + ATTR_FONT_CROSSEDOUT) ); + + if (rEditSet.GetItemState(EE_CHAR_ITALIC,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxPostureItem( (FontItalic)((const SvxPostureItem*)pItem)->GetValue(), + ATTR_FONT_POSTURE) ); + if (rEditSet.GetItemState(EE_CHAR_ITALIC_CJK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxPostureItem( (FontItalic)((const SvxPostureItem*)pItem)->GetValue(), + ATTR_CJK_FONT_POSTURE) ); + if (rEditSet.GetItemState(EE_CHAR_ITALIC_CTL,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxPostureItem( (FontItalic)((const SvxPostureItem*)pItem)->GetValue(), + ATTR_CTL_FONT_POSTURE) ); + + if (rEditSet.GetItemState(EE_CHAR_OUTLINE,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxContourItem( ((const SvxContourItem*)pItem)->GetValue(), + ATTR_FONT_CONTOUR) ); + if (rEditSet.GetItemState(EE_CHAR_SHADOW,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxShadowedItem( ((const SvxShadowedItem*)pItem)->GetValue(), + ATTR_FONT_SHADOWED) ); + if (rEditSet.GetItemState(EE_CHAR_EMPHASISMARK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxEmphasisMarkItem( ((const SvxEmphasisMarkItem*)pItem)->GetEmphasisMark(), + ATTR_FONT_EMPHASISMARK) ); + if (rEditSet.GetItemState(EE_CHAR_RELIEF,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxCharReliefItem( (FontRelief)((const SvxCharReliefItem*)pItem)->GetValue(), + ATTR_FONT_RELIEF) ); + + if (rEditSet.GetItemState(EE_CHAR_LANGUAGE,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxLanguageItem(static_cast<const SvxLanguageItem*>(pItem)->GetValue(), ATTR_FONT_LANGUAGE) ); + if (rEditSet.GetItemState(EE_CHAR_LANGUAGE_CJK,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxLanguageItem(static_cast<const SvxLanguageItem*>(pItem)->GetValue(), ATTR_CJK_FONT_LANGUAGE) ); + if (rEditSet.GetItemState(EE_CHAR_LANGUAGE_CTL,TRUE,&pItem) == SFX_ITEM_SET) + rDestSet.Put( SvxLanguageItem(static_cast<const SvxLanguageItem*>(pItem)->GetValue(), ATTR_CTL_FONT_LANGUAGE) ); + + if (rEditSet.GetItemState(EE_PARA_JUST,TRUE,&pItem) == SFX_ITEM_SET) + { + SvxCellHorJustify eVal; + switch ( ((const SvxAdjustItem*)pItem)->GetAdjust() ) + { + case SVX_ADJUST_LEFT: + // #30154# EditEngine Default ist bei dem GetAttribs() ItemSet + // immer gesetzt! + // ob links oder rechts entscheiden wir selbst bei Text/Zahl + eVal = SVX_HOR_JUSTIFY_STANDARD; + break; + case SVX_ADJUST_RIGHT: + eVal = SVX_HOR_JUSTIFY_RIGHT; + break; + case SVX_ADJUST_BLOCK: + eVal = SVX_HOR_JUSTIFY_BLOCK; + break; + case SVX_ADJUST_CENTER: + eVal = SVX_HOR_JUSTIFY_CENTER; + break; + case SVX_ADJUST_BLOCKLINE: + eVal = SVX_HOR_JUSTIFY_BLOCK; + break; + case SVX_ADJUST_END: + eVal = SVX_HOR_JUSTIFY_RIGHT; + break; + default: + eVal = SVX_HOR_JUSTIFY_STANDARD; + } + if ( eVal != SVX_HOR_JUSTIFY_STANDARD ) + rDestSet.Put( SvxHorJustifyItem( eVal, ATTR_HOR_JUSTIFY) ); + } +} + +void ScPatternAttr::GetFromEditItemSet( const SfxItemSet* pEditSet ) +{ + if( pEditSet ) + GetFromEditItemSet( GetItemSet(), *pEditSet ); +} + +void ScPatternAttr::FillEditParaItems( SfxItemSet* pEditSet ) const +{ + // in GetFromEditItemSet schon dabei, in FillEditItemSet aber nicht + // Hor. Ausrichtung Standard wird immer als "links" umgesetzt + + const SfxItemSet& rMySet = GetItemSet(); + + SvxCellHorJustify eHorJust = (SvxCellHorJustify) + ((const SvxHorJustifyItem&)rMySet.Get(ATTR_HOR_JUSTIFY)).GetValue(); + + SvxAdjust eSvxAdjust; + switch (eHorJust) + { + case SVX_HOR_JUSTIFY_RIGHT: eSvxAdjust = SVX_ADJUST_RIGHT; break; + case SVX_HOR_JUSTIFY_CENTER: eSvxAdjust = SVX_ADJUST_CENTER; break; + case SVX_HOR_JUSTIFY_BLOCK: eSvxAdjust = SVX_ADJUST_BLOCK; break; + default: eSvxAdjust = SVX_ADJUST_LEFT; break; + } + pEditSet->Put( SvxAdjustItem( eSvxAdjust, EE_PARA_JUST ) ); +} + +void ScPatternAttr::DeleteUnchanged( const ScPatternAttr* pOldAttrs ) +{ + SfxItemSet& rThisSet = GetItemSet(); + const SfxItemSet& rOldSet = pOldAttrs->GetItemSet(); + + const SfxPoolItem* pThisItem; + const SfxPoolItem* pOldItem; + + for ( USHORT nSubWhich=ATTR_PATTERN_START; nSubWhich<=ATTR_PATTERN_END; nSubWhich++ ) + { + // only items that are set are interesting + if ( rThisSet.GetItemState( nSubWhich, FALSE, &pThisItem ) == SFX_ITEM_SET ) + { + SfxItemState eOldState = rOldSet.GetItemState( nSubWhich, TRUE, &pOldItem ); + if ( eOldState == SFX_ITEM_SET ) + { + // item is set in OldAttrs (or its parent) -> compare pointers + if ( pThisItem == pOldItem ) + rThisSet.ClearItem( nSubWhich ); + } + else if ( eOldState != SFX_ITEM_DONTCARE ) + { + // not set in OldAttrs -> compare item value to default item + if ( *pThisItem == rThisSet.GetPool()->GetDefaultItem( nSubWhich ) ) + rThisSet.ClearItem( nSubWhich ); + } + } + } +} + +BOOL ScPatternAttr::HasItemsSet( const USHORT* pWhich ) const +{ + const SfxItemSet& rSet = GetItemSet(); + for (USHORT i=0; pWhich[i]; i++) + if ( rSet.GetItemState( pWhich[i], FALSE ) == SFX_ITEM_SET ) + return TRUE; + return FALSE; +} + +void ScPatternAttr::ClearItems( const USHORT* pWhich ) +{ + SfxItemSet& rSet = GetItemSet(); + for (USHORT i=0; pWhich[i]; i++) + rSet.ClearItem(pWhich[i]); +} + +SfxStyleSheetBase* lcl_CopyStyleToPool + ( + SfxStyleSheetBase* pSrcStyle, + SfxStyleSheetBasePool* pSrcPool, + SfxStyleSheetBasePool* pDestPool, + const SvNumberFormatterIndexTable* pFormatExchangeList + ) +{ + if ( !pSrcStyle || !pDestPool || !pSrcPool ) + { + DBG_ERROR( "CopyStyleToPool: Invalid Arguments :-/" ); + return NULL; + } + + //-------------------------------------------------------- + + const String aStrSrcStyle = pSrcStyle->GetName(); + const SfxStyleFamily eFamily = pSrcStyle->GetFamily(); + SfxStyleSheetBase* pDestStyle = pDestPool->Find( aStrSrcStyle, eFamily ); + + if ( !pDestStyle ) + { + const String aStrParent = pSrcStyle->GetParent(); + const SfxItemSet& rSrcSet = pSrcStyle->GetItemSet(); + + pDestStyle = &pDestPool->Make( aStrSrcStyle, eFamily, SFXSTYLEBIT_USERDEF ); + SfxItemSet& rDestSet = pDestStyle->GetItemSet(); + rDestSet.Put( rSrcSet ); + + // #b5017505# number format exchange list has to be handled here, too + // (only called for cell styles) + + const SfxPoolItem* pSrcItem; + if ( pFormatExchangeList && + rSrcSet.GetItemState( ATTR_VALUE_FORMAT, FALSE, &pSrcItem ) == SFX_ITEM_SET ) + { + ULONG nOldFormat = static_cast<const SfxUInt32Item*>(pSrcItem)->GetValue(); + sal_uInt32* pNewFormat = static_cast<sal_uInt32*>(pFormatExchangeList->Get( nOldFormat )); + if (pNewFormat) + rDestSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, *pNewFormat ) ); + } + + // ggF. abgeleitete Styles erzeugen, wenn nicht vorhanden: + + if ( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) != aStrParent && + aStrSrcStyle != aStrParent && + !pDestPool->Find( aStrParent, eFamily ) ) + { + lcl_CopyStyleToPool( pSrcPool->Find( aStrParent, eFamily ), + pSrcPool, pDestPool, pFormatExchangeList ); + } + + pDestStyle->SetParent( aStrParent ); + } + + return pDestStyle; +} + +ScPatternAttr* ScPatternAttr::PutInPool( ScDocument* pDestDoc, ScDocument* pSrcDoc ) const +{ + const SfxItemSet* pSrcSet = &GetItemSet(); + + ScPatternAttr* pDestPattern = new ScPatternAttr(pDestDoc->GetPool()); + SfxItemSet* pDestSet = &pDestPattern->GetItemSet(); + + // Zellformatvorlage in anderes Dokument kopieren: + + if ( pDestDoc != pSrcDoc ) + { + DBG_ASSERT( pStyle, "Missing Pattern-Style! :-/" ); + + // wenn Vorlage im DestDoc vorhanden, dieses benutzen, sonst Style + // mit Parent-Vorlagen kopieren/ggF. erzeugen und dem DestDoc hinzufuegen + + SfxStyleSheetBase* pStyleCpy = lcl_CopyStyleToPool( pStyle, + pSrcDoc->GetStyleSheetPool(), + pDestDoc->GetStyleSheetPool(), + pDestDoc->GetFormatExchangeList() ); + + pDestPattern->SetStyleSheet( (ScStyleSheet*)pStyleCpy ); + } + + for ( USHORT nAttrId = ATTR_PATTERN_START; nAttrId <= ATTR_PATTERN_END; nAttrId++ ) + { + const SfxPoolItem* pSrcItem; + SfxItemState eItemState = pSrcSet->GetItemState( nAttrId, FALSE, &pSrcItem ); + if (eItemState==SFX_ITEM_ON) + { + SfxPoolItem* pNewItem = NULL; + + if ( nAttrId == ATTR_CONDITIONAL ) + { + // Bedingte Formate ins neue Dokument kopieren + + ULONG nNewIndex = 0; + ScConditionalFormatList* pSrcList = pSrcDoc->GetCondFormList(); + if ( pSrcList ) + { + ULONG nOldIndex = ((const SfxUInt32Item*)pSrcItem)->GetValue(); + const ScConditionalFormat* pOldData = pSrcList->GetFormat( nOldIndex ); + if ( pOldData ) + { + nNewIndex = pDestDoc->AddCondFormat( *pOldData ); + + // zugehoerige Styles auch mitkopieren + //! nur wenn Format bei Add neu angelegt + + ScStyleSheetPool* pSrcSPool = pSrcDoc->GetStyleSheetPool(); + ScStyleSheetPool* pDestSPool = pDestDoc->GetStyleSheetPool(); + const SvNumberFormatterIndexTable* pFormatExchangeList = pDestDoc->GetFormatExchangeList(); + USHORT nStlCnt = pOldData->Count(); + for (USHORT i=0; i<nStlCnt; i++) + { + String aName = pOldData->GetEntry(i)->GetStyle(); + SfxStyleSheetBase* pSrcStl = + pSrcDoc->GetStyleSheetPool()->Find(aName, SFX_STYLE_FAMILY_PARA); + lcl_CopyStyleToPool( pSrcStl, pSrcSPool, pDestSPool, pFormatExchangeList ); + } + } + } + pNewItem = new SfxUInt32Item( ATTR_CONDITIONAL, nNewIndex ); + } + else if ( nAttrId == ATTR_VALIDDATA ) + { + // Gueltigkeit ins neue Dokument kopieren + + ULONG nNewIndex = 0; + ScValidationDataList* pSrcList = pSrcDoc->GetValidationList(); + if ( pSrcList ) + { + ULONG nOldIndex = ((const SfxUInt32Item*)pSrcItem)->GetValue(); + const ScValidationData* pOldData = pSrcList->GetData( nOldIndex ); + if ( pOldData ) + nNewIndex = pDestDoc->AddValidationEntry( *pOldData ); + } + pNewItem = new SfxUInt32Item( ATTR_VALIDDATA, nNewIndex ); + } + else if ( nAttrId == ATTR_VALUE_FORMAT && pDestDoc->GetFormatExchangeList() ) + { + // Zahlformate nach Exchange-Liste + + ULONG nOldFormat = ((const SfxUInt32Item*)pSrcItem)->GetValue(); + sal_uInt32* pNewFormat = static_cast<sal_uInt32*>(pDestDoc->GetFormatExchangeList()->Get(nOldFormat)); + if (pNewFormat) + pNewItem = new SfxUInt32Item( ATTR_VALUE_FORMAT, (UINT32) (*pNewFormat) ); + } + + if ( pNewItem ) + { + pDestSet->Put(*pNewItem); + delete pNewItem; + } + else + pDestSet->Put(*pSrcItem); + } + } + + ScPatternAttr* pPatternAttr = + (ScPatternAttr*) &pDestDoc->GetPool()->Put(*pDestPattern); + delete pDestPattern; + return pPatternAttr; +} + +BOOL ScPatternAttr::IsVisible() const +{ + const SfxItemSet& rSet = GetItemSet(); + + const SfxPoolItem* pItem; + SfxItemState eState; + + eState = rSet.GetItemState( ATTR_BACKGROUND, TRUE, &pItem ); + if ( eState == SFX_ITEM_SET ) + if ( ((const SvxBrushItem*)pItem)->GetColor().GetColor() != COL_TRANSPARENT ) + return TRUE; + + eState = rSet.GetItemState( ATTR_BORDER, TRUE, &pItem ); + if ( eState == SFX_ITEM_SET ) + { + const SvxBoxItem* pBoxItem = (SvxBoxItem*) pItem; + if ( pBoxItem->GetTop() || pBoxItem->GetBottom() || + pBoxItem->GetLeft() || pBoxItem->GetRight() ) + return TRUE; + } + + eState = rSet.GetItemState( ATTR_BORDER_TLBR, TRUE, &pItem ); + if ( eState == SFX_ITEM_SET ) + if( static_cast< const SvxLineItem* >( pItem )->GetLine() ) + return TRUE; + + eState = rSet.GetItemState( ATTR_BORDER_BLTR, TRUE, &pItem ); + if ( eState == SFX_ITEM_SET ) + if( static_cast< const SvxLineItem* >( pItem )->GetLine() ) + return TRUE; + + eState = rSet.GetItemState( ATTR_SHADOW, TRUE, &pItem ); + if ( eState == SFX_ITEM_SET ) + if ( ((const SvxShadowItem*)pItem)->GetLocation() != SVX_SHADOW_NONE ) + return TRUE; + + return FALSE; +} + +inline BOOL OneEqual( const SfxItemSet& rSet1, const SfxItemSet& rSet2, USHORT nId ) +{ + const SfxPoolItem* pItem1 = &rSet1.Get(nId); + const SfxPoolItem* pItem2 = &rSet2.Get(nId); + return ( pItem1 == pItem2 || *pItem1 == *pItem2 ); +} + +BOOL ScPatternAttr::IsVisibleEqual( const ScPatternAttr& rOther ) const +{ + const SfxItemSet& rThisSet = GetItemSet(); + const SfxItemSet& rOtherSet = rOther.GetItemSet(); + + return OneEqual( rThisSet, rOtherSet, ATTR_BACKGROUND ) && + OneEqual( rThisSet, rOtherSet, ATTR_BORDER ) && + OneEqual( rThisSet, rOtherSet, ATTR_BORDER_TLBR ) && + OneEqual( rThisSet, rOtherSet, ATTR_BORDER_BLTR ) && + OneEqual( rThisSet, rOtherSet, ATTR_SHADOW ); + + //! auch hier nur wirklich sichtbare Werte testen !!! +} + +const String* ScPatternAttr::GetStyleName() const +{ + return pName ? pName : ( pStyle ? &pStyle->GetName() : NULL ); +} + + +void ScPatternAttr::SetStyleSheet( ScStyleSheet* pNewStyle ) +{ + if (pNewStyle) + { + SfxItemSet& rPatternSet = GetItemSet(); + const SfxItemSet& rStyleSet = pNewStyle->GetItemSet(); + + for (USHORT i=ATTR_PATTERN_START; i<=ATTR_PATTERN_END; i++) + { + if (rStyleSet.GetItemState(i, TRUE) == SFX_ITEM_SET) + rPatternSet.ClearItem(i); + } + rPatternSet.SetParent(&pNewStyle->GetItemSet()); + pStyle = pNewStyle; + DELETEZ( pName ); + } + else + { + DBG_ERROR( "ScPatternAttr::SetStyleSheet( NULL ) :-|" ); + GetItemSet().SetParent(NULL); + pStyle = NULL; + } +} + +void ScPatternAttr::UpdateStyleSheet() +{ + if (pName) + { + pStyle = (ScStyleSheet*)pDoc->GetStyleSheetPool()->Find(*pName, SFX_STYLE_FAMILY_PARA); + + // wenn Style nicht gefunden, Standard nehmen, + // damit keine leere Anzeige im Toolbox-Controller + //! es wird vorausgesetzt, dass "Standard" immer der erste Eintrag ist! + if (!pStyle) + { + SfxStyleSheetIterator* pIter = pDoc->GetStyleSheetPool()->CreateIterator( + SFX_STYLE_FAMILY_PARA, SFXSTYLEBIT_ALL ); + pStyle = (ScStyleSheet*)pIter->First(); + } + + if (pStyle) + { + GetItemSet().SetParent(&pStyle->GetItemSet()); + DELETEZ( pName ); + } + } + else + pStyle = NULL; +} + +void ScPatternAttr::StyleToName() +{ + // Style wurde geloescht, Namen merken: + + if ( pStyle ) + { + if ( pName ) + *pName = pStyle->GetName(); + else + pName = new String( pStyle->GetName() ); + + pStyle = NULL; + GetItemSet().SetParent( NULL ); + } +} + +BOOL ScPatternAttr::IsSymbolFont() const +{ + const SfxPoolItem* pItem; + if( GetItemSet().GetItemState( ATTR_FONT, TRUE, &pItem ) == SFX_ITEM_SET ) + return BOOL( ((const SvxFontItem*) pItem)->GetCharSet() + == RTL_TEXTENCODING_SYMBOL ); + else + return FALSE; +} + +//UNUSED2008-05 FontToSubsFontConverter ScPatternAttr::GetSubsFontConverter( ULONG nFlags ) const +//UNUSED2008-05 { +//UNUSED2008-05 const SfxPoolItem* pItem; +//UNUSED2008-05 if( GetItemSet().GetItemState( ATTR_FONT, TRUE, &pItem ) == SFX_ITEM_SET ) +//UNUSED2008-05 return CreateFontToSubsFontConverter( +//UNUSED2008-05 ((const SvxFontItem*) pItem)->GetFamilyName(), nFlags ); +//UNUSED2008-05 else +//UNUSED2008-05 return 0; +//UNUSED2008-05 } + + +ULONG ScPatternAttr::GetNumberFormat( SvNumberFormatter* pFormatter ) const +{ + ULONG nFormat = + ((SfxUInt32Item*)&GetItemSet().Get( ATTR_VALUE_FORMAT ))->GetValue(); + LanguageType eLang = + ((SvxLanguageItem*)&GetItemSet().Get( ATTR_LANGUAGE_FORMAT ))->GetLanguage(); + if ( nFormat < SV_COUNTRY_LANGUAGE_OFFSET && eLang == LANGUAGE_SYSTEM ) + ; // es bleibt wie es ist + else if ( pFormatter ) + nFormat = pFormatter->GetFormatForLanguageIfBuiltIn( nFormat, eLang ); + return nFormat; +} + +// dasselbe, wenn bedingte Formatierung im Spiel ist: + +ULONG ScPatternAttr::GetNumberFormat( SvNumberFormatter* pFormatter, + const SfxItemSet* pCondSet ) const +{ + DBG_ASSERT(pFormatter,"GetNumberFormat ohne Formatter"); + + const SfxPoolItem* pFormItem; + if ( !pCondSet || pCondSet->GetItemState(ATTR_VALUE_FORMAT,TRUE,&pFormItem) != SFX_ITEM_SET ) + pFormItem = &GetItemSet().Get(ATTR_VALUE_FORMAT); + + const SfxPoolItem* pLangItem; + if ( !pCondSet || pCondSet->GetItemState(ATTR_LANGUAGE_FORMAT,TRUE,&pLangItem) != SFX_ITEM_SET ) + pLangItem = &GetItemSet().Get(ATTR_LANGUAGE_FORMAT); + + return pFormatter->GetFormatForLanguageIfBuiltIn( + ((SfxUInt32Item*)pFormItem)->GetValue(), + ((SvxLanguageItem*)pLangItem)->GetLanguage() ); +} + +const SfxPoolItem& ScPatternAttr::GetItem( USHORT nWhich, const SfxItemSet& rItemSet, const SfxItemSet* pCondSet ) +{ + const SfxPoolItem* pCondItem; + if ( pCondSet && pCondSet->GetItemState( nWhich, TRUE, &pCondItem ) == SFX_ITEM_SET ) + return *pCondItem; + return rItemSet.Get(nWhich); +} + +const SfxPoolItem& ScPatternAttr::GetItem( USHORT nSubWhich, const SfxItemSet* pCondSet ) const +{ + return GetItem( nSubWhich, GetItemSet(), pCondSet ); +} + +// GetRotateVal testet vorher ATTR_ORIENTATION + +long ScPatternAttr::GetRotateVal( const SfxItemSet* pCondSet ) const +{ + long nAttrRotate = 0; + if ( GetCellOrientation() == SVX_ORIENTATION_STANDARD ) + { + BOOL bRepeat = ( static_cast<const SvxHorJustifyItem&>(GetItem(ATTR_HOR_JUSTIFY, pCondSet)). + GetValue() == SVX_HOR_JUSTIFY_REPEAT ); + // ignore orientation/rotation if "repeat" is active + if ( !bRepeat ) + nAttrRotate = ((const SfxInt32Item&)GetItem( ATTR_ROTATE_VALUE, pCondSet )).GetValue(); + } + return nAttrRotate; +} + +BYTE ScPatternAttr::GetRotateDir( const SfxItemSet* pCondSet ) const +{ + BYTE nRet = SC_ROTDIR_NONE; + + long nAttrRotate = GetRotateVal( pCondSet ); + if ( nAttrRotate ) + { + SvxRotateMode eRotMode = (SvxRotateMode)((const SvxRotateModeItem&) + GetItem(ATTR_ROTATE_MODE, pCondSet)).GetValue(); + + if ( eRotMode == SVX_ROTATE_MODE_STANDARD || nAttrRotate == 18000 ) + nRet = SC_ROTDIR_STANDARD; + else if ( eRotMode == SVX_ROTATE_MODE_CENTER ) + nRet = SC_ROTDIR_CENTER; + else if ( eRotMode == SVX_ROTATE_MODE_TOP || eRotMode == SVX_ROTATE_MODE_BOTTOM ) + { + long nRot180 = nAttrRotate % 18000; // 1/100 Grad + if ( nRot180 == 9000 ) + nRet = SC_ROTDIR_CENTER; + else if ( ( eRotMode == SVX_ROTATE_MODE_TOP && nRot180 < 9000 ) || + ( eRotMode == SVX_ROTATE_MODE_BOTTOM && nRot180 > 9000 ) ) + nRet = SC_ROTDIR_LEFT; + else + nRet = SC_ROTDIR_RIGHT; + } + } + + return nRet; +} + + + + diff --git a/sc/source/core/data/pivot.cxx b/sc/source/core/data/pivot.cxx new file mode 100644 index 000000000000..00cdf2a84eac --- /dev/null +++ b/sc/source/core/data/pivot.cxx @@ -0,0 +1,37 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pivot.cxx,v $ + * $Revision: 1.13 $ + * + * 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" + + + +// ----------------------------------------------------------------------- + diff --git a/sc/source/core/data/pivot2.cxx b/sc/source/core/data/pivot2.cxx new file mode 100644 index 000000000000..e12df0cda6e5 --- /dev/null +++ b/sc/source/core/data/pivot2.cxx @@ -0,0 +1,94 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: pivot2.cxx,v $ + * $Revision: 1.14.32.3 $ + * + * 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" + + + +#ifdef _MSC_VER +#pragma optimize("",off) +#endif + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svx/boxitem.hxx> +#include <svx/wghtitem.hxx> +#include <svx/algitem.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "globstr.hrc" +#include "subtotal.hxx" +#include "rangeutl.hxx" +#include "attrib.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "document.hxx" +#include "userlist.hxx" +#include "pivot.hxx" +#include "rechead.hxx" +#include "formula/errorcodes.hxx" // fuer errNoValue +#include "refupdat.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" + +using ::com::sun::star::sheet::DataPilotFieldReference; + +// STATIC DATA ----------------------------------------------------------- +// ============================================================================ + +LabelData::LabelData( const String& rName, short nCol, bool bIsValue ) : + maName( rName ), + mnCol( nCol ), + mnFuncMask( PIVOT_FUNC_NONE ), + mnUsedHier( 0 ), + mbShowAll( false ), + mbIsValue( bIsValue ) +{ +} + +// ============================================================================ + +ScDPFuncData::ScDPFuncData( short nCol, USHORT nFuncMask ) : + mnCol( nCol ), + mnFuncMask( nFuncMask ) +{ +} + +ScDPFuncData::ScDPFuncData( short nCol, USHORT nFuncMask, const DataPilotFieldReference& rFieldRef ) : + mnCol( nCol ), + mnFuncMask( nFuncMask ), + maFieldRef( rFieldRef ) +{ +} + +// ============================================================================ + diff --git a/sc/source/core/data/poolhelp.cxx b/sc/source/core/data/poolhelp.cxx new file mode 100644 index 000000000000..d85e16ad135f --- /dev/null +++ b/sc/source/core/data/poolhelp.cxx @@ -0,0 +1,131 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: poolhelp.cxx,v $ + * $Revision: 1.8 $ + * + * 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 <svtools/zforlist.hxx> +#include <svx/editeng.hxx> + +#include "poolhelp.hxx" +#include "document.hxx" +#include "docpool.hxx" +#include "stlpool.hxx" + +// ----------------------------------------------------------------------- + +ScPoolHelper::ScPoolHelper( ScDocument* pSourceDoc ) +:pFormTable(NULL) +,pEditPool(NULL) +,pEnginePool(NULL) +,m_pSourceDoc(pSourceDoc) +{ + DBG_ASSERT( pSourceDoc, "ScPoolHelper: no document" ); + pDocPool = new ScDocumentPool; + pDocPool->FreezeIdRanges(); + + mxStylePool = new ScStyleSheetPool( *pDocPool, pSourceDoc ); +} + +ScPoolHelper::~ScPoolHelper() +{ + SfxItemPool::Free(pEnginePool); + SfxItemPool::Free(pEditPool); + delete pFormTable; + mxStylePool.clear(); + SfxItemPool::Free(pDocPool); +} +SfxItemPool* ScPoolHelper::GetEditPool() const +{ + if ( !pEditPool ) + { + pEditPool = EditEngine::CreatePool(); + pEditPool->SetDefaultMetric( SFX_MAPUNIT_100TH_MM ); + pEditPool->FreezeIdRanges(); + pEditPool->SetFileFormatVersion( SOFFICE_FILEFORMAT_50 ); // used in ScGlobal::EETextObjEqual + } + return pEditPool; +} +SfxItemPool* ScPoolHelper::GetEnginePool() const +{ + if ( !pEnginePool ) + { + pEnginePool = EditEngine::CreatePool(); + pEnginePool->SetDefaultMetric( SFX_MAPUNIT_100TH_MM ); + pEnginePool->FreezeIdRanges(); + } // ifg ( pEnginePool ) + return pEnginePool; +} +SvNumberFormatter* ScPoolHelper::GetFormTable() const +{ + if ( !pFormTable ) + { + pFormTable = new SvNumberFormatter( m_pSourceDoc->GetServiceManager(), ScGlobal::eLnge ); + pFormTable->SetColorLink( LINK( m_pSourceDoc, ScDocument, GetUserDefinedColor ) ); + pFormTable->SetEvalDateFormat( NF_EVALDATEFORMAT_INTL_FORMAT ); + + UseDocOptions(); // null date, year2000, std precision + } + return pFormTable; +} + +void ScPoolHelper::UseDocOptions() const +{ + if (pFormTable) + { + USHORT d,m,y; + aOpt.GetDate( d,m,y ); + pFormTable->ChangeNullDate( d,m,y ); + pFormTable->ChangeStandardPrec( (USHORT)aOpt.GetStdPrecision() ); + pFormTable->SetYear2000( aOpt.GetYear2000() ); + } +} + +void ScPoolHelper::SetFormTableOpt(const ScDocOptions& rOpt) +{ + aOpt = rOpt; + UseDocOptions(); // #i105512# if the number formatter exists, update its settings +} + +void ScPoolHelper::SourceDocumentGone() +{ + // reset all pointers to the source document + mxStylePool->SetDocument( NULL ); + if ( pFormTable ) + pFormTable->SetColorLink( Link() ); +} + +// ----------------------------------------------------------------------- + + diff --git a/sc/source/core/data/postit.cxx b/sc/source/core/data/postit.cxx new file mode 100644 index 000000000000..7a08f352ed91 --- /dev/null +++ b/sc/source/core/data/postit.cxx @@ -0,0 +1,923 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: postit.cxx,v $ + * $Revision: 1.12.54.11 $ + * + * 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 "postit.hxx" + +#include <rtl/ustrbuf.hxx> +#include <svtools/useroptions.hxx> +#include <svx/svdpage.hxx> +#include <svx/svdocapt.hxx> +#include <svx/outlobj.hxx> +#include <svx/editobj.hxx> +#include <basegfx/polygon/b2dpolygon.hxx> + +#include "scitems.hxx" +#include <svx/xlnstit.hxx> +#include <svx/xlnstwit.hxx> +#include <svx/xlnstcit.hxx> +#include <svx/sxcecitm.hxx> +#include <svx/xflclit.hxx> +#include <svx/sdshitm.hxx> +#include <svx/sdsxyitm.hxx> + +#include "document.hxx" +#include "docpool.hxx" +#include "patattr.hxx" +#include "cell.hxx" +#include "drwlayer.hxx" +#include "userdat.hxx" +#include "detfunc.hxx" + +using ::rtl::OUString; +using ::rtl::OUStringBuffer; + +// ============================================================================ + +namespace { + +const long SC_NOTECAPTION_WIDTH = 2900; /// Default width of note caption textbox. +const long SC_NOTECAPTION_MAXWIDTH_TEMP = 12000; /// Maximum width of temporary note caption textbox. +const long SC_NOTECAPTION_HEIGHT = 1800; /// Default height of note caption textbox. +const long SC_NOTECAPTION_CELLDIST = 600; /// Default distance of note captions to border of anchor cell. +const long SC_NOTECAPTION_OFFSET_Y = -1500; /// Default Y offset of note captions to top border of anchor cell. +const long SC_NOTECAPTION_OFFSET_X = 1500; /// Default X offset of note captions to left border of anchor cell. +const long SC_NOTECAPTION_BORDERDIST_TEMP = 100; /// Distance of temporary note captions to visible sheet area. + +// ============================================================================ + +/** Static helper functions for caption objects. */ +class ScCaptionUtil +{ +public: + /** Moves the caption object to the correct layer according to passed visibility. */ + static void SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown ); + /** Sets basic caption settings required for note caption objects. */ + static void SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown ); + /** Stores the cell position of the note in the user data area of the caption. */ + static void SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos ); + /** Sets all default formatting attributes to the caption object. */ + static void SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc ); + /** Updates caption item set according to the passed item set while removing shadow items. */ + static void SetCaptionItems( SdrCaptionObj& rCaption, const SfxItemSet& rItemSet ); +}; + +// ---------------------------------------------------------------------------- + +void ScCaptionUtil::SetCaptionLayer( SdrCaptionObj& rCaption, bool bShown ) +{ + SdrLayerID nLayer = bShown ? SC_LAYER_INTERN : SC_LAYER_HIDDEN; + if( nLayer != rCaption.GetLayer() ) + rCaption.SetLayer( nLayer ); +} + +void ScCaptionUtil::SetBasicCaptionSettings( SdrCaptionObj& rCaption, bool bShown ) +{ + ScDrawLayer::SetAnchor( &rCaption, SCA_PAGE ); + SetCaptionLayer( rCaption, bShown ); + rCaption.SetFixedTail(); + rCaption.SetSpecialTextBoxShadow(); +} + +void ScCaptionUtil::SetCaptionUserData( SdrCaptionObj& rCaption, const ScAddress& rPos ) +{ + // pass true to ScDrawLayer::GetObjData() to create the object data entry + ScDrawObjData* pObjData = ScDrawLayer::GetObjData( &rCaption, true ); + OSL_ENSURE( pObjData, "ScCaptionUtil::SetCaptionUserData - missing drawing object user data" ); + pObjData->maStart = rPos; + pObjData->mbNote = true; +} + +void ScCaptionUtil::SetDefaultItems( SdrCaptionObj& rCaption, ScDocument& rDoc ) +{ + SfxItemSet aItemSet = rCaption.GetMergedItemSet(); + + // caption tail arrow + ::basegfx::B2DPolygon aTriangle; + aTriangle.append( ::basegfx::B2DPoint( 10.0, 0.0 ) ); + aTriangle.append( ::basegfx::B2DPoint( 0.0, 30.0 ) ); + aTriangle.append( ::basegfx::B2DPoint( 20.0, 30.0 ) ); + aTriangle.setClosed( true ); + /* #99319# Line ends are now created with an empty name. The + checkForUniqueItem() method then finds a unique name for the item's + value. */ + aItemSet.Put( XLineStartItem( String::EmptyString(), ::basegfx::B2DPolyPolygon( aTriangle ) ) ); + aItemSet.Put( XLineStartWidthItem( 200 ) ); + aItemSet.Put( XLineStartCenterItem( FALSE ) ); + aItemSet.Put( XFillStyleItem( XFILL_SOLID ) ); + aItemSet.Put( XFillColorItem( String::EmptyString(), ScDetectiveFunc::GetCommentColor() ) ); + aItemSet.Put( SdrCaptionEscDirItem( SDRCAPT_ESCBESTFIT ) ); + + // shadow + /* SdrShadowItem has FALSE, instead the shadow is set for the + rectangle only with SetSpecialTextBoxShadow() when the object is + created (item must be set to adjust objects from older files). */ + aItemSet.Put( SdrShadowItem( FALSE ) ); + aItemSet.Put( SdrShadowXDistItem( 100 ) ); + aItemSet.Put( SdrShadowYDistItem( 100 ) ); + + // text attributes + aItemSet.Put( SdrTextLeftDistItem( 100 ) ); + aItemSet.Put( SdrTextRightDistItem( 100 ) ); + aItemSet.Put( SdrTextUpperDistItem( 100 ) ); + aItemSet.Put( SdrTextLowerDistItem( 100 ) ); + aItemSet.Put( SdrTextAutoGrowWidthItem( FALSE ) ); + aItemSet.Put( SdrTextAutoGrowHeightItem( TRUE ) ); + // #78943# use the default cell style to be able to modify the caption font + const ScPatternAttr& rDefPattern = static_cast< const ScPatternAttr& >( rDoc.GetPool()->GetDefaultItem( ATTR_PATTERN ) ); + rDefPattern.FillEditItemSet( &aItemSet ); + + rCaption.SetMergedItemSet( aItemSet ); +} + +void ScCaptionUtil::SetCaptionItems( SdrCaptionObj& rCaption, const SfxItemSet& rItemSet ) +{ + // copy all items + rCaption.SetMergedItemSet( rItemSet ); + // reset shadow items + rCaption.SetMergedItem( SdrShadowItem( FALSE ) ); + rCaption.SetMergedItem( SdrShadowXDistItem( 100 ) ); + rCaption.SetMergedItem( SdrShadowYDistItem( 100 ) ); + rCaption.SetSpecialTextBoxShadow(); +} + +// ============================================================================ + +/** Helper for creation and manipulation of caption drawing objects independent + from cell annotations. */ +class ScCaptionCreator +{ +public: + /** Create a new caption. The caption will not be inserted into the document. */ + explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bShown, bool bTailFront ); + /** Manipulate an existing caption. */ + explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption ); + + /** Returns the drawing layer page of the sheet contained in maPos. */ + SdrPage* GetDrawPage(); + /** Returns the caption drawing obejct. */ + inline SdrCaptionObj* GetCaption() { return mpCaption; } + + /** Moves the caption inside the passed rectangle. Uses page area if 0 is passed. */ + void FitCaptionToRect( const Rectangle* pVisRect = 0 ); + /** Places the caption inside the passed rectangle, tries to keep the cell rectangle uncovered. Uses page area if 0 is passed. */ + void AutoPlaceCaption( const Rectangle* pVisRect = 0 ); + /** Updates caption tail and textbox according to current cell position. Uses page area if 0 is passed. */ + void UpdateCaptionPos( const Rectangle* pVisRect = 0 ); + +protected: + /** Helper constructor for derived classes. */ + explicit ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos ); + + /** Calculates the caption tail position according to current cell position. */ + Point CalcTailPos( bool bTailFront ); + /** Implements creation of the caption object. The caption will not be inserted into the document. */ + void CreateCaption( bool bShown, bool bTailFront ); + +private: + /** Initializes all members. */ + void Initialize(); + /** Returns the passed rectangle if existing, page rectangle otherwise. */ + inline const Rectangle& GetVisRect( const Rectangle* pVisRect ) const { return pVisRect ? *pVisRect : maPageRect; } + +private: + ScDocument& mrDoc; + ScAddress maPos; + SdrCaptionObj* mpCaption; + Rectangle maPageRect; + Rectangle maCellRect; + bool mbNegPage; +}; + +// ---------------------------------------------------------------------------- + +ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, bool bShown, bool bTailFront ) : + mrDoc( rDoc ), + maPos( rPos ), + mpCaption( 0 ) +{ + Initialize(); + CreateCaption( bShown, bTailFront ); +} + +ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption ) : + mrDoc( rDoc ), + maPos( rPos ), + mpCaption( &rCaption ) +{ + Initialize(); +} + +ScCaptionCreator::ScCaptionCreator( ScDocument& rDoc, const ScAddress& rPos ) : + mrDoc( rDoc ), + maPos( rPos ), + mpCaption( 0 ) +{ + Initialize(); +} + +SdrPage* ScCaptionCreator::GetDrawPage() +{ + ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer(); + return pDrawLayer ? pDrawLayer->GetPage( static_cast< sal_uInt16 >( maPos.Tab() ) ) : 0; +} + +void ScCaptionCreator::FitCaptionToRect( const Rectangle* pVisRect ) +{ + const Rectangle& rVisRect = GetVisRect( pVisRect ); + + // tail position + Point aTailPos = mpCaption->GetTailPos(); + aTailPos.X() = ::std::max( ::std::min( aTailPos.X(), rVisRect.Right() ), rVisRect.Left() ); + aTailPos.Y() = ::std::max( ::std::min( aTailPos.Y(), rVisRect.Bottom() ), rVisRect.Top() ); + mpCaption->SetTailPos( aTailPos ); + + // caption rectangle + Rectangle aCaptRect = mpCaption->GetLogicRect(); + Point aCaptPos = aCaptRect.TopLeft(); + // move textbox inside right border of visible area + aCaptPos.X() = ::std::min< long >( aCaptPos.X(), rVisRect.Right() - aCaptRect.GetWidth() ); + // move textbox inside left border of visible area (this may move it outside on right side again) + aCaptPos.X() = ::std::max< long >( aCaptPos.X(), rVisRect.Left() ); + // move textbox inside bottom border of visible area + aCaptPos.Y() = ::std::min< long >( aCaptPos.Y(), rVisRect.Bottom() - aCaptRect.GetHeight() ); + // move textbox inside top border of visible area (this may move it outside on bottom side again) + aCaptPos.Y() = ::std::max< long >( aCaptPos.Y(), rVisRect.Top() ); + // update caption + aCaptRect.SetPos( aCaptPos ); + mpCaption->SetLogicRect( aCaptRect ); +} + +void ScCaptionCreator::AutoPlaceCaption( const Rectangle* pVisRect ) +{ + const Rectangle& rVisRect = GetVisRect( pVisRect ); + + // caption rectangle + Rectangle aCaptRect = mpCaption->GetLogicRect(); + long nWidth = aCaptRect.GetWidth(); + long nHeight = aCaptRect.GetHeight(); + + // n***Space contains available space between border of visible area and cell + long nLeftSpace = maCellRect.Left() - rVisRect.Left() + 1; + long nRightSpace = rVisRect.Right() - maCellRect.Right() + 1; + long nTopSpace = maCellRect.Top() - rVisRect.Top() + 1; + long nBottomSpace = rVisRect.Bottom() - maCellRect.Bottom() + 1; + + // nNeeded*** contains textbox dimensions plus needed distances to cell or border of visible area + long nNeededSpaceX = nWidth + SC_NOTECAPTION_CELLDIST; + long nNeededSpaceY = nHeight + SC_NOTECAPTION_CELLDIST; + + // bFitsWidth*** == true means width of textbox fits into horizontal free space of visible area + bool bFitsWidthLeft = nNeededSpaceX <= nLeftSpace; // text box width fits into the width left of cell + bool bFitsWidthRight = nNeededSpaceX <= nRightSpace; // text box width fits into the width right of cell + bool bFitsWidth = nWidth <= rVisRect.GetWidth(); // text box width fits into width of visible area + + // bFitsHeight*** == true means height of textbox fits into vertical free space of visible area + bool bFitsHeightTop = nNeededSpaceY <= nTopSpace; // text box height fits into the height above cell + bool bFitsHeightBottom = nNeededSpaceY <= nBottomSpace; // text box height fits into the height below cell + bool bFitsHeight = nHeight <= rVisRect.GetHeight(); // text box height fits into height of visible area + + // bFits*** == true means the textbox fits completely into free space of visible area + bool bFitsLeft = bFitsWidthLeft && bFitsHeight; + bool bFitsRight = bFitsWidthRight && bFitsHeight; + bool bFitsTop = bFitsWidth && bFitsHeightTop; + bool bFitsBottom = bFitsWidth && bFitsHeightBottom; + + Point aCaptPos; + // use left/right placement if possible, or if top/bottom placement not possible + if( bFitsLeft || bFitsRight || (!bFitsTop && !bFitsBottom) ) + { + // prefer left in RTL sheet and right in LTR sheets + bool bPreferLeft = bFitsLeft && (mbNegPage || !bFitsRight); + bool bPreferRight = bFitsRight && (!mbNegPage || !bFitsLeft); + // move to left, if left is preferred, or if neither left nor right fit and there is more space to the left + if( bPreferLeft || (!bPreferRight && (nLeftSpace > nRightSpace)) ) + aCaptPos.X() = maCellRect.Left() - SC_NOTECAPTION_CELLDIST - nWidth; + else // to right + aCaptPos.X() = maCellRect.Right() + SC_NOTECAPTION_CELLDIST; + // Y position according to top cell border + aCaptPos.Y() = maCellRect.Top() + SC_NOTECAPTION_OFFSET_Y; + } + else // top or bottom placement + { + // X position + aCaptPos.X() = maCellRect.Left() + SC_NOTECAPTION_OFFSET_X; + // top placement, if possible + if( bFitsTop ) + aCaptPos.Y() = maCellRect.Top() - SC_NOTECAPTION_CELLDIST - nHeight; + else // bottom placement + aCaptPos.Y() = maCellRect.Bottom() + SC_NOTECAPTION_CELLDIST; + } + + // update textbox position in note caption object + aCaptRect.SetPos( aCaptPos ); + mpCaption->SetLogicRect( aCaptRect ); + FitCaptionToRect( pVisRect ); +} + +void ScCaptionCreator::UpdateCaptionPos( const Rectangle* pVisRect ) +{ + ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer(); + + // update caption position + const Point& rOldTailPos = mpCaption->GetTailPos(); + Point aTailPos = CalcTailPos( false ); + if( rOldTailPos != aTailPos ) + { + // create drawing undo action + if( pDrawLayer && pDrawLayer->IsRecording() ) + pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoGeoObject( *mpCaption ) ); + // calculate new caption rectangle (#i98141# handle LTR<->RTL switch correctly) + Rectangle aCaptRect = mpCaption->GetLogicRect(); + long nDiffX = (rOldTailPos.X() >= 0) ? (aCaptRect.Left() - rOldTailPos.X()) : (rOldTailPos.X() - aCaptRect.Right()); + if( mbNegPage ) nDiffX = -nDiffX - aCaptRect.GetWidth(); + long nDiffY = aCaptRect.Top() - rOldTailPos.Y(); + aCaptRect.SetPos( aTailPos + Point( nDiffX, nDiffY ) ); + // set new tail position and caption rectangle + mpCaption->SetTailPos( aTailPos ); + mpCaption->SetLogicRect( aCaptRect ); + // fit caption into draw page + FitCaptionToRect( pVisRect ); + } + + // update cell position in caption user data + ScDrawObjData* pCaptData = ScDrawLayer::GetNoteCaptionData( mpCaption, maPos.Tab() ); + if( pCaptData && (maPos != pCaptData->maStart) ) + { + // create drawing undo action + if( pDrawLayer && pDrawLayer->IsRecording() ) + pDrawLayer->AddCalcUndo( new ScUndoObjData( mpCaption, pCaptData->maStart, pCaptData->maEnd, maPos, pCaptData->maEnd ) ); + // set new position + pCaptData->maStart = maPos; + } +} + +Point ScCaptionCreator::CalcTailPos( bool bTailFront ) +{ + // tail position + bool bTailLeft = bTailFront != mbNegPage; + Point aTailPos = bTailLeft ? maCellRect.TopLeft() : maCellRect.TopRight(); + // move caption point 1/10 mm inside cell + if( bTailLeft ) aTailPos.X() += 10; else aTailPos.X() -= 10; + aTailPos.Y() += 10; + return aTailPos; +} + +void ScCaptionCreator::CreateCaption( bool bShown, bool bTailFront ) +{ + // create the caption drawing object + Rectangle aTextRect( Point( 0 , 0 ), Size( SC_NOTECAPTION_WIDTH, SC_NOTECAPTION_HEIGHT ) ); + Point aTailPos = CalcTailPos( bTailFront ); + mpCaption = new SdrCaptionObj( aTextRect, aTailPos ); + // basic caption settings + ScCaptionUtil::SetBasicCaptionSettings( *mpCaption, bShown ); +} + +void ScCaptionCreator::Initialize() +{ + maCellRect = ScDrawLayer::GetCellRect( mrDoc, maPos, true ); + mbNegPage = mrDoc.IsNegativePage( maPos.Tab() ); + if( SdrPage* pDrawPage = GetDrawPage() ) + { + maPageRect = Rectangle( Point( 0, 0 ), pDrawPage->GetSize() ); + /* #i98141# SdrPage::GetSize() returns negative width in RTL mode. + The call to Rectangle::Adjust() orders left/right coordinate + accordingly. */ + maPageRect.Justify(); + } +} + +// ============================================================================ + +/** Helper for creation of permanent caption drawing objects for cell notes. */ +class ScNoteCaptionCreator : public ScCaptionCreator +{ +public: + /** Create a new caption object and inserts it into the document. */ + explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData ); + /** Manipulate an existing caption. */ + explicit ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown ); +}; + +// ---------------------------------------------------------------------------- + +ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, ScNoteData& rNoteData ) : + ScCaptionCreator( rDoc, rPos ) // use helper c'tor that does not create the caption yet +{ + SdrPage* pDrawPage = GetDrawPage(); + OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" ); + if( pDrawPage ) + { + // create the caption drawing object + CreateCaption( rNoteData.mbShown, false ); + rNoteData.mpCaption = GetCaption(); + OSL_ENSURE( rNoteData.mpCaption, "ScNoteCaptionCreator::ScNoteCaptionCreator - missing caption object" ); + if( rNoteData.mpCaption ) + { + // store note position in user data of caption object + ScCaptionUtil::SetCaptionUserData( *rNoteData.mpCaption, rPos ); + // insert object into draw page + pDrawPage->InsertObject( rNoteData.mpCaption ); + } + } +} + +ScNoteCaptionCreator::ScNoteCaptionCreator( ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown ) : + ScCaptionCreator( rDoc, rPos, rCaption ) +{ + SdrPage* pDrawPage = GetDrawPage(); + OSL_ENSURE( pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - no drawing page" ); + OSL_ENSURE( rCaption.GetPage() == pDrawPage, "ScNoteCaptionCreator::ScNoteCaptionCreator - wrong drawing page in caption" ); + if( pDrawPage && (rCaption.GetPage() == pDrawPage) ) + { + // store note position in user data of caption object + ScCaptionUtil::SetCaptionUserData( rCaption, rPos ); + // basic caption settings + ScCaptionUtil::SetBasicCaptionSettings( rCaption, bShown ); + // set correct tail position + rCaption.SetTailPos( CalcTailPos( false ) ); + } +} + +} // namespace + +// ============================================================================ + +struct ScCaptionInitData +{ + typedef ::std::auto_ptr< SfxItemSet > SfxItemSetPtr; + typedef ::std::auto_ptr< OutlinerParaObject > OutlinerParaObjPtr; + + SfxItemSetPtr mxItemSet; /// Caption object formatting. + OutlinerParaObjPtr mxOutlinerObj; /// Text object with all text portion formatting. + ::rtl::OUString maSimpleText; /// Simple text without formatting. + Point maCaptionOffset; /// Caption position relative to cell corner. + Size maCaptionSize; /// Size of the caption object. + bool mbDefaultPosSize; /// True = use default position and size for caption. + + explicit ScCaptionInitData(); +}; + +// ---------------------------------------------------------------------------- + +ScCaptionInitData::ScCaptionInitData() : + mbDefaultPosSize( true ) +{ +} + +// ============================================================================ + +ScNoteData::ScNoteData( bool bShown ) : + mpCaption( 0 ), + mbShown( bShown ) +{ +} + +ScNoteData::~ScNoteData() +{ +} + +// ============================================================================ + +ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, bool bShown ) : + mrDoc( rDoc ), + maNoteData( bShown ) +{ + AutoStamp(); + CreateCaption( rPos ); +} + +ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScPostIt& rNote ) : + mrDoc( rDoc ), + maNoteData( rNote.maNoteData ) +{ + maNoteData.mpCaption = 0; + CreateCaption( rPos, rNote.maNoteData.mpCaption ); +} + +ScPostIt::ScPostIt( ScDocument& rDoc, const ScAddress& rPos, const ScNoteData& rNoteData, bool bAlwaysCreateCaption ) : + mrDoc( rDoc ), + maNoteData( rNoteData ) +{ + if( bAlwaysCreateCaption || maNoteData.mbShown ) + CreateCaptionFromInitData( rPos ); +} + +ScPostIt::~ScPostIt() +{ + RemoveCaption(); +} + +ScPostIt* ScPostIt::Clone( const ScAddress& rOwnPos, ScDocument& rDestDoc, const ScAddress& rDestPos, bool bCloneCaption ) const +{ + CreateCaptionFromInitData( rOwnPos ); + return bCloneCaption ? new ScPostIt( rDestDoc, rDestPos, *this ) : new ScPostIt( rDestDoc, rDestPos, maNoteData, false ); +} + +void ScPostIt::AutoStamp() +{ + maNoteData.maDate = ScGlobal::pLocaleData->getDate( Date() ); + maNoteData.maAuthor = SvtUserOptions().GetID(); +} + +const OutlinerParaObject* ScPostIt::GetOutlinerObject() const +{ + if( maNoteData.mpCaption ) + return maNoteData.mpCaption->GetOutlinerParaObject(); + if( maNoteData.mxInitData.get() ) + return maNoteData.mxInitData->mxOutlinerObj.get(); + return 0; +} + +const EditTextObject* ScPostIt::GetEditTextObject() const +{ + const OutlinerParaObject* pOPO = GetOutlinerObject(); + return pOPO ? &pOPO->GetTextObject() : 0; +} + +OUString ScPostIt::GetText() const +{ + if( const EditTextObject* pEditObj = GetEditTextObject() ) + { + OUStringBuffer aBuffer; + for( USHORT nPara = 0, nParaCount = pEditObj->GetParagraphCount(); nPara < nParaCount; ++nPara ) + { + if( nPara > 0 ) + aBuffer.append( sal_Unicode( '\n' ) ); + aBuffer.append( pEditObj->GetText( nPara ) ); + } + return aBuffer.makeStringAndClear(); + } + if( maNoteData.mxInitData.get() ) + return maNoteData.mxInitData->maSimpleText; + return OUString(); +} + +bool ScPostIt::HasMultiLineText() const +{ + if( const EditTextObject* pEditObj = GetEditTextObject() ) + return pEditObj->GetParagraphCount() > 1; + if( maNoteData.mxInitData.get() ) + return maNoteData.mxInitData->maSimpleText.indexOf( '\n' ) >= 0; + return false; +} + +void ScPostIt::SetText( const ScAddress& rPos, const OUString& rText ) +{ + CreateCaptionFromInitData( rPos ); + if( maNoteData.mpCaption ) + maNoteData.mpCaption->SetText( rText ); +} + +SdrCaptionObj* ScPostIt::GetOrCreateCaption( const ScAddress& rPos ) const +{ + CreateCaptionFromInitData( rPos ); + return maNoteData.mpCaption; +} + +void ScPostIt::ForgetCaption() +{ + /* This function is used in undo actions to give up the responsibility for + the caption object which is handled by separate drawing undo actions. */ + maNoteData.mpCaption = 0; + maNoteData.mxInitData.reset(); +} + +void ScPostIt::ShowCaption( const ScAddress& rPos, bool bShow ) +{ + CreateCaptionFromInitData( rPos ); + // no separate drawing undo needed, handled completely inside ScUndoShowHideNote + maNoteData.mbShown = bShow; + if( maNoteData.mpCaption ) + ScCaptionUtil::SetCaptionLayer( *maNoteData.mpCaption, bShow ); +} + +void ScPostIt::ShowCaptionTemp( const ScAddress& rPos, bool bShow ) +{ + CreateCaptionFromInitData( rPos ); + if( maNoteData.mpCaption ) + ScCaptionUtil::SetCaptionLayer( *maNoteData.mpCaption, maNoteData.mbShown || bShow ); +} + +void ScPostIt::UpdateCaptionPos( const ScAddress& rPos ) +{ + CreateCaptionFromInitData( rPos ); + if( maNoteData.mpCaption ) + { + ScCaptionCreator aCreator( mrDoc, rPos, *maNoteData.mpCaption ); + aCreator.UpdateCaptionPos(); + } +} + +// private -------------------------------------------------------------------- + +void ScPostIt::CreateCaptionFromInitData( const ScAddress& rPos ) const +{ + OSL_ENSURE( maNoteData.mpCaption || maNoteData.mxInitData.get(), "ScPostIt::CreateCaptionFromInitData - need caption object or initial caption data" ); + if( maNoteData.mxInitData.get() ) + { + /* This function is called from ScPostIt::Clone() when copying cells + to the clipboard/undo document, and when copying cells from the + clipboard/undo document. The former should always be called first, + so if called in an clipboard/undo document, the caption should have + been created already. */ + OSL_ENSURE( !mrDoc.IsUndo() && !mrDoc.IsClipboard(), "ScPostIt::CreateCaptionFromInitData - note caption should not be created in undo/clip documents" ); + + /* #i104915# Never try to create notes in Undo document, leads to + crash due to missing document members (e.g. row height array). */ + if( !maNoteData.mpCaption && !mrDoc.IsUndo() ) + { + // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData + ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData ); + if( maNoteData.mpCaption ) + { + ScCaptionInitData& rInitData = *maNoteData.mxInitData; + + // transfer ownership of outliner object to caption, or set simple text + OSL_ENSURE( rInitData.mxOutlinerObj.get() || (rInitData.maSimpleText.getLength() > 0), + "ScPostIt::CreateCaptionFromInitData - need either outliner para object or simple text" ); + if( rInitData.mxOutlinerObj.get() ) + maNoteData.mpCaption->SetOutlinerParaObject( rInitData.mxOutlinerObj.release() ); + else + maNoteData.mpCaption->SetText( rInitData.maSimpleText ); + + // copy all items or set default items; reset shadow items + ScCaptionUtil::SetDefaultItems( *maNoteData.mpCaption, mrDoc ); + if( rInitData.mxItemSet.get() ) + ScCaptionUtil::SetCaptionItems( *maNoteData.mpCaption, *rInitData.mxItemSet ); + + // set position and size of the caption object + if( rInitData.mbDefaultPosSize ) + { + // set other items and fit caption size to text + maNoteData.mpCaption->SetMergedItem( SdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) ); + maNoteData.mpCaption->SetMergedItem( SdrTextMaxFrameWidthItem( SC_NOTECAPTION_MAXWIDTH_TEMP ) ); + maNoteData.mpCaption->AdjustTextFrameWidthAndHeight(); + aCreator.AutoPlaceCaption(); + } + else + { + Rectangle aCellRect = ScDrawLayer::GetCellRect( mrDoc, rPos, true ); + bool bNegPage = mrDoc.IsNegativePage( rPos.Tab() ); + long nPosX = bNegPage ? (aCellRect.Left() - rInitData.maCaptionOffset.X()) : (aCellRect.Right() + rInitData.maCaptionOffset.X()); + long nPosY = aCellRect.Top() + rInitData.maCaptionOffset.Y(); + Rectangle aCaptRect( Point( nPosX, nPosY ), rInitData.maCaptionSize ); + maNoteData.mpCaption->SetLogicRect( aCaptRect ); + aCreator.FitCaptionToRect(); + } + } + } + // forget the initial caption data struct + maNoteData.mxInitData.reset(); + } +} + +void ScPostIt::CreateCaption( const ScAddress& rPos, const SdrCaptionObj* pCaption ) +{ + OSL_ENSURE( !maNoteData.mpCaption, "ScPostIt::CreateCaption - unexpected caption object found" ); + maNoteData.mpCaption = 0; + + /* #i104915# Never try to create notes in Undo document, leads to + crash due to missing document members (e.g. row height array). */ + OSL_ENSURE( !mrDoc.IsUndo(), "ScPostIt::CreateCaption - note caption should not be created in undo documents" ); + if( mrDoc.IsUndo() ) + return; + + // drawing layer may be missing, if a note is copied into a clipboard document + if( mrDoc.IsClipboard() ) + mrDoc.InitDrawLayer(); + + // ScNoteCaptionCreator c'tor creates the caption and inserts it into the document and maNoteData + ScNoteCaptionCreator aCreator( mrDoc, rPos, maNoteData ); + if( maNoteData.mpCaption ) + { + // clone settings of passed caption + if( pCaption ) + { + // copy edit text object (object must be inserted into page already) + if( OutlinerParaObject* pOPO = pCaption->GetOutlinerParaObject() ) + maNoteData.mpCaption->SetOutlinerParaObject( new OutlinerParaObject( *pOPO ) ); + // copy formatting items (after text has been copied to apply font formatting) + maNoteData.mpCaption->SetMergedItemSetAndBroadcast( pCaption->GetMergedItemSet() ); + // move textbox position relative to new cell, copy textbox size + Rectangle aCaptRect = pCaption->GetLogicRect(); + Point aDist = maNoteData.mpCaption->GetTailPos() - pCaption->GetTailPos(); + aCaptRect.Move( aDist.X(), aDist.Y() ); + maNoteData.mpCaption->SetLogicRect( aCaptRect ); + aCreator.FitCaptionToRect(); + } + else + { + // set default formatting and default position + ScCaptionUtil::SetDefaultItems( *maNoteData.mpCaption, mrDoc ); + aCreator.AutoPlaceCaption(); + } + + // create undo action + if( ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer() ) + if( pDrawLayer->IsRecording() ) + pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoNewObject( *maNoteData.mpCaption ) ); + } +} + +void ScPostIt::RemoveCaption() +{ + + /* Remove caption object only, if this note is its owner (e.g. notes in + undo documents refer to captions in original document, do not remove + them from drawing layer here). */ + ScDrawLayer* pDrawLayer = mrDoc.GetDrawLayer(); + if( maNoteData.mpCaption && (pDrawLayer == maNoteData.mpCaption->GetModel()) ) + { + OSL_ENSURE( pDrawLayer, "ScPostIt::RemoveCaption - object without drawing layer" ); + SdrPage* pDrawPage = maNoteData.mpCaption->GetPage(); + OSL_ENSURE( pDrawPage, "ScPostIt::RemoveCaption - object without drawing page" ); + if( pDrawPage ) + { + pDrawPage->RecalcObjOrdNums(); + // create drawing undo action (before removing the object to have valid draw page in undo action) + if( pDrawLayer && pDrawLayer->IsRecording() ) + pDrawLayer->AddCalcUndo( pDrawLayer->GetSdrUndoFactory().CreateUndoDeleteObject( *maNoteData.mpCaption ) ); + // remove the object from the drawing page, delete if undo is disabled + pDrawPage->RemoveObject( maNoteData.mpCaption->GetOrdNum() ); + } + } + maNoteData.mpCaption = 0; +} + +// ============================================================================ + +void ScNoteUtil::UpdateCaptionPositions( ScDocument& rDoc, const ScRange& rRange ) +{ + // do not use ScCellIterator, it skips filtered and subtotal cells + for( ScAddress aPos( rRange.aStart ); aPos.Tab() <= rRange.aEnd.Tab(); aPos.IncTab() ) + for( aPos.SetCol( rRange.aStart.Col() ); aPos.Col() <= rRange.aEnd.Col(); aPos.IncCol() ) + for( aPos.SetRow( rRange.aStart.Row() ); aPos.Row() <= rRange.aEnd.Row(); aPos.IncRow() ) + if( ScPostIt* pNote = rDoc.GetNote( aPos ) ) + pNote->UpdateCaptionPos( aPos ); +} + +SdrCaptionObj* ScNoteUtil::CreateTempCaption( + ScDocument& rDoc, const ScAddress& rPos, SdrPage& rDrawPage, + const OUString& rUserText, const Rectangle& rVisRect, bool bTailFront ) +{ + OUStringBuffer aBuffer( rUserText ); + // add plain text of invisible (!) cell note (no formatting etc.) + SdrCaptionObj* pNoteCaption = 0; + const ScPostIt* pNote = rDoc.GetNote( rPos ); + if( pNote && !pNote->IsCaptionShown() ) + { + if( aBuffer.getLength() > 0 ) + aBuffer.appendAscii( RTL_CONSTASCII_STRINGPARAM( "\n--------\n" ) ).append( pNote->GetText() ); + pNoteCaption = pNote->GetOrCreateCaption( rPos ); + } + + // create a caption if any text exists + if( !pNoteCaption && (aBuffer.getLength() == 0) ) + return 0; + + // prepare visible rectangle (add default distance to all borders) + Rectangle aVisRect( + rVisRect.Left() + SC_NOTECAPTION_BORDERDIST_TEMP, + rVisRect.Top() + SC_NOTECAPTION_BORDERDIST_TEMP, + rVisRect.Right() - SC_NOTECAPTION_BORDERDIST_TEMP, + rVisRect.Bottom() - SC_NOTECAPTION_BORDERDIST_TEMP ); + + // create the caption object + ScCaptionCreator aCreator( rDoc, rPos, true, bTailFront ); + SdrCaptionObj* pCaption = aCreator.GetCaption(); + + // insert caption into page (needed to set caption text) + rDrawPage.InsertObject( pCaption ); + + // clone the edit text object, unless user text is present, then set this text + if( pNoteCaption && (rUserText.getLength() == 0) ) + { + if( OutlinerParaObject* pOPO = pNoteCaption->GetOutlinerParaObject() ) + pCaption->SetOutlinerParaObject( new OutlinerParaObject( *pOPO ) ); + // set formatting (must be done after setting text) and resize the box to fit the text + pCaption->SetMergedItemSetAndBroadcast( pNoteCaption->GetMergedItemSet() ); + Rectangle aCaptRect( pCaption->GetLogicRect().TopLeft(), pNoteCaption->GetLogicRect().GetSize() ); + pCaption->SetLogicRect( aCaptRect ); + } + else + { + // if pNoteCaption is null, then aBuffer contains some text + pCaption->SetText( aBuffer.makeStringAndClear() ); + ScCaptionUtil::SetDefaultItems( *pCaption, rDoc ); + // adjust caption size to text size + long nMaxWidth = ::std::min< long >( aVisRect.GetWidth() * 2 / 3, SC_NOTECAPTION_MAXWIDTH_TEMP ); + pCaption->SetMergedItem( SdrTextAutoGrowWidthItem( TRUE ) ); + pCaption->SetMergedItem( SdrTextMinFrameWidthItem( SC_NOTECAPTION_WIDTH ) ); + pCaption->SetMergedItem( SdrTextMaxFrameWidthItem( nMaxWidth ) ); + pCaption->SetMergedItem( SdrTextAutoGrowHeightItem( TRUE ) ); + pCaption->AdjustTextFrameWidthAndHeight(); + } + + // move caption into visible area + aCreator.AutoPlaceCaption( &aVisRect ); + return pCaption; +} + +ScPostIt* ScNoteUtil::CreateNoteFromCaption( + ScDocument& rDoc, const ScAddress& rPos, SdrCaptionObj& rCaption, bool bShown ) +{ + ScNoteData aNoteData( bShown ); + aNoteData.mpCaption = &rCaption; + ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, false ); + pNote->AutoStamp(); + rDoc.TakeNote( rPos, pNote ); + // if pNote still points to the note after TakeNote(), insertion was successful + if( pNote ) + { + // ScNoteCaptionCreator c'tor updates the caption object to be part of a note + ScNoteCaptionCreator aCreator( rDoc, rPos, rCaption, bShown ); + } + return pNote; +} + +ScPostIt* ScNoteUtil::CreateNoteFromObjectData( + ScDocument& rDoc, const ScAddress& rPos, SfxItemSet* pItemSet, + OutlinerParaObject* pOutlinerObj, const Rectangle& rCaptionRect, + bool bShown, bool bAlwaysCreateCaption ) +{ + OSL_ENSURE( pItemSet && pOutlinerObj, "ScNoteUtil::CreateNoteFromObjectData - item set and outliner object expected" ); + ScNoteData aNoteData( bShown ); + aNoteData.mxInitData.reset( new ScCaptionInitData ); + ScCaptionInitData& rInitData = *aNoteData.mxInitData; + rInitData.mxItemSet.reset( pItemSet ); + rInitData.mxOutlinerObj.reset( pOutlinerObj ); + + // convert absolute caption position to relative position + rInitData.mbDefaultPosSize = rCaptionRect.IsEmpty(); + if( !rInitData.mbDefaultPosSize ) + { + Rectangle aCellRect = ScDrawLayer::GetCellRect( rDoc, rPos, true ); + bool bNegPage = rDoc.IsNegativePage( rPos.Tab() ); + rInitData.maCaptionOffset.X() = bNegPage ? (aCellRect.Left() - rCaptionRect.Right()) : (rCaptionRect.Left() - aCellRect.Right()); + rInitData.maCaptionOffset.Y() = rCaptionRect.Top() - aCellRect.Top(); + rInitData.maCaptionSize = rCaptionRect.GetSize(); + } + + /* Create the note and insert it into the document. If the note is + visible, the caption object will be created automatically. */ + ScPostIt* pNote = new ScPostIt( rDoc, rPos, aNoteData, bAlwaysCreateCaption ); + pNote->AutoStamp(); + rDoc.TakeNote( rPos, pNote ); + // if pNote still points to the note after TakeNote(), insertion was successful + return pNote; +} + +ScPostIt* ScNoteUtil::CreateNoteFromString( + ScDocument& rDoc, const ScAddress& rPos, const OUString& rNoteText, + bool bShown, bool bAlwaysCreateCaption ) +{ + ScPostIt* pNote = 0; + if( rNoteText.getLength() > 0 ) + { + ScNoteData aNoteData( bShown ); + aNoteData.mxInitData.reset( new ScCaptionInitData ); + ScCaptionInitData& rInitData = *aNoteData.mxInitData; + rInitData.maSimpleText = rNoteText; + rInitData.mbDefaultPosSize = true; + + /* Create the note and insert it into the document. If the note is + visible, the caption object will be created automatically. */ + pNote = new ScPostIt( rDoc, rPos, aNoteData, bAlwaysCreateCaption ); + pNote->AutoStamp(); + rDoc.TakeNote( rPos, pNote ); + // if pNote still points to the note after TakeNote(), insertion was successful + } + return pNote; +} + +// ============================================================================ diff --git a/sc/source/core/data/scimpexpmsg.cxx b/sc/source/core/data/scimpexpmsg.cxx new file mode 100644 index 000000000000..00dde7d9fa2a --- /dev/null +++ b/sc/source/core/data/scimpexpmsg.cxx @@ -0,0 +1,116 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: scimpexpmsg.cxx,v $ + * $Revision: 1.5.32.3 $ + * + * 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 "document.hxx" +#include "scimpexpmsg.hxx" + +#include <tools/string.hxx> + +//UNUSED2008-05 ScImpExpLogMsg::ScImpExpLogMsg( ScImpExpMsg e ) : eId( e ), pPos( NULL ), pHint( NULL ) +//UNUSED2008-05 { +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 ScImpExpLogMsg::ScImpExpLogMsg( ScImpExpMsg e, const String& r ) : eId( e ), pHint( NULL ) +//UNUSED2008-05 { +//UNUSED2008-05 pPos = new String( r ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 ScImpExpLogMsg::ScImpExpLogMsg( ScImpExpMsg e, const String& rP, const String& rH ) : eId( e ) +//UNUSED2008-05 { +//UNUSED2008-05 pPos = new String( rP ); +//UNUSED2008-05 pHint = new String( rH ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 ScImpExpLogMsg::ScImpExpLogMsg( const ScImpExpLogMsg& r ) : eId( r.eId ) +//UNUSED2008-05 { +//UNUSED2008-05 if( r.pPos ) +//UNUSED2008-05 pPos = new String( *r.pPos ); +//UNUSED2008-05 else +//UNUSED2008-05 pPos = NULL; +//UNUSED2008-05 +//UNUSED2008-05 if( r.pHint ) +//UNUSED2008-05 pHint = new String( *r.pHint ); +//UNUSED2008-05 else +//UNUSED2008-05 pHint = NULL; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 ScImpExpLogMsg::~ScImpExpLogMsg() +//UNUSED2008-05 { +//UNUSED2008-05 if( pPos ) +//UNUSED2008-05 delete pPos; +//UNUSED2008-05 +//UNUSED2008-05 if( pHint ) +//UNUSED2008-05 delete pHint; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 void ScImpExpLogMsg::Set( ScImpExpMsg e, const String* pP, const String* pH ) +//UNUSED2008-05 { +//UNUSED2008-05 eId = e; +//UNUSED2008-05 if( pPos ) +//UNUSED2008-05 delete pPos; +//UNUSED2008-05 +//UNUSED2008-05 if( pHint ) +//UNUSED2008-05 delete pHint; +//UNUSED2008-05 +//UNUSED2008-05 if( pP ) +//UNUSED2008-05 pPos = new String( *pP ); +//UNUSED2008-05 else +//UNUSED2008-05 pPos = NULL; +//UNUSED2008-05 +//UNUSED2008-05 if( pH ) +//UNUSED2008-05 pHint = new String( *pH ); +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 +//UNUSED2008-05 String ScImpExpLogMsg::GetMsg( ScImpExpMsg e ) +//UNUSED2008-05 { +//UNUSED2008-05 const sal_Char* p; +//UNUSED2008-05 switch( e ) +//UNUSED2008-05 { +//UNUSED2008-05 case SC_IMPEXPMSG_UNKNOWN: p = "unknown log message"; break; +//UNUSED2008-05 default: p = "Not specified type of log message"; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 String aRet; +//UNUSED2008-05 aRet.AssignAscii( p ); +//UNUSED2008-05 return aRet; +//UNUSED2008-05 } + diff --git a/sc/source/core/data/sortparam.cxx b/sc/source/core/data/sortparam.cxx new file mode 100644 index 000000000000..15691a642d7e --- /dev/null +++ b/sc/source/core/data/sortparam.cxx @@ -0,0 +1,265 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: sortparam.cxx,v $ + * $Revision: 1.8.146.1 $ + * + * 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 "sortparam.hxx" +#include "global.hxx" +#include "address.hxx" +#include <tools/debug.hxx> + + +//------------------------------------------------------------------------ + +ScSortParam::ScSortParam() +{ + Clear(); +} + +//------------------------------------------------------------------------ + +ScSortParam::ScSortParam( const ScSortParam& r ) : + nCol1(r.nCol1),nRow1(r.nRow1),nCol2(r.nCol2),nRow2(r.nRow2), + bHasHeader(r.bHasHeader),bByRow(r.bByRow),bCaseSens(r.bCaseSens), + bUserDef(r.bUserDef),nUserIndex(r.nUserIndex),bIncludePattern(r.bIncludePattern), + bInplace(r.bInplace), + nDestTab(r.nDestTab),nDestCol(r.nDestCol),nDestRow(r.nDestRow), + aCollatorLocale( r.aCollatorLocale ), aCollatorAlgorithm( r.aCollatorAlgorithm ) +{ + for (USHORT i=0; i<MAXSORT; i++) + { + bDoSort[i] = r.bDoSort[i]; + nField[i] = r.nField[i]; + bAscending[i] = r.bAscending[i]; + } +} + +//------------------------------------------------------------------------ + +void ScSortParam::Clear() +{ + nCol1=nCol2=nDestCol = 0; + nRow1=nRow2=nDestRow = 0; + nCompatHeader = 2; + nDestTab = 0; + nUserIndex = 0; + bHasHeader=bCaseSens=bUserDef = FALSE; + bByRow=bIncludePattern=bInplace = TRUE; + aCollatorLocale = ::com::sun::star::lang::Locale(); + aCollatorAlgorithm.Erase(); + + for (USHORT i=0; i<MAXSORT; i++) + { + bDoSort[i] = FALSE; + nField[i] = 0; + bAscending[i] = TRUE; + } +} + +//------------------------------------------------------------------------ + +ScSortParam& ScSortParam::operator=( const ScSortParam& r ) +{ + nCol1 = r.nCol1; + nRow1 = r.nRow1; + nCol2 = r.nCol2; + nRow2 = r.nRow2; + bHasHeader = r.bHasHeader; + bCaseSens = r.bCaseSens; + bByRow = r.bByRow; + bUserDef = r.bUserDef; + nUserIndex = r.nUserIndex; + bIncludePattern = r.bIncludePattern; + bInplace = r.bInplace; + nDestTab = r.nDestTab; + nDestCol = r.nDestCol; + nDestRow = r.nDestRow; + aCollatorLocale = r.aCollatorLocale; + aCollatorAlgorithm = r.aCollatorAlgorithm; + + for (USHORT i=0; i<MAXSORT; i++) + { + bDoSort[i] = r.bDoSort[i]; + nField[i] = r.nField[i]; + bAscending[i] = r.bAscending[i]; + } + + return *this; +} + +//------------------------------------------------------------------------ + +BOOL ScSortParam::operator==( const ScSortParam& rOther ) const +{ + BOOL bEqual = FALSE; + // Anzahl der Sorts gleich? + USHORT nLast = 0; + USHORT nOtherLast = 0; + while ( bDoSort[nLast++] && nLast < MAXSORT ) ; + while ( rOther.bDoSort[nOtherLast++] && nOtherLast < MAXSORT ) ; + nLast--; + nOtherLast--; + if ( (nLast == nOtherLast) + && (nCol1 == rOther.nCol1) + && (nRow1 == rOther.nRow1) + && (nCol2 == rOther.nCol2) + && (nRow2 == rOther.nRow2) + && (bHasHeader == rOther.bHasHeader) + && (bByRow == rOther.bByRow) + && (bCaseSens == rOther.bCaseSens) + && (bUserDef == rOther.bUserDef) + && (nUserIndex == rOther.nUserIndex) + && (bIncludePattern == rOther.bIncludePattern) + && (bInplace == rOther.bInplace) + && (nDestTab == rOther.nDestTab) + && (nDestCol == rOther.nDestCol) + && (nDestRow == rOther.nDestRow) + && (aCollatorLocale.Language == rOther.aCollatorLocale.Language) + && (aCollatorLocale.Country == rOther.aCollatorLocale.Country) + && (aCollatorLocale.Variant == rOther.aCollatorLocale.Variant) + && (aCollatorAlgorithm == rOther.aCollatorAlgorithm) + ) + { + bEqual = TRUE; + for ( USHORT i=0; i<=nLast && bEqual; i++ ) + { + bEqual = (nField[i] == rOther.nField[i]) && (bAscending[i] == rOther.bAscending[i]); + } + } + return bEqual; +} + +//------------------------------------------------------------------------ + +ScSortParam::ScSortParam( const ScSubTotalParam& rSub, const ScSortParam& rOld ) : + nCol1(rSub.nCol1),nRow1(rSub.nRow1),nCol2(rSub.nCol2),nRow2(rSub.nRow2), + bHasHeader(TRUE),bByRow(TRUE),bCaseSens(rSub.bCaseSens), + bUserDef(rSub.bUserDef),nUserIndex(rSub.nUserIndex),bIncludePattern(rSub.bIncludePattern), + bInplace(TRUE), + nDestTab(0),nDestCol(0),nDestRow(0), + aCollatorLocale( rOld.aCollatorLocale ), aCollatorAlgorithm( rOld.aCollatorAlgorithm ) +{ + USHORT nNewCount = 0; + USHORT i; + + // zuerst die Gruppen aus den Teilergebnissen + if (rSub.bDoSort) + for (i=0; i<MAXSUBTOTAL; i++) + if (rSub.bGroupActive[i]) + { + if (nNewCount < MAXSORT) + { + bDoSort[nNewCount] = TRUE; + nField[nNewCount] = rSub.nField[i]; + bAscending[nNewCount] = rSub.bAscending; + ++nNewCount; + } + } + + // dann dahinter die alten Einstellungen + for (i=0; i<MAXSORT; i++) + if (rOld.bDoSort[i]) + { + SCCOLROW nThisField = rOld.nField[i]; + BOOL bDouble = FALSE; + for (USHORT j=0; j<nNewCount; j++) + if ( nField[j] == nThisField ) + bDouble = TRUE; + if (!bDouble) // ein Feld nicht zweimal eintragen + { + if (nNewCount < MAXSORT) + { + bDoSort[nNewCount] = TRUE; + nField[nNewCount] = nThisField; + bAscending[nNewCount] = rOld.bAscending[i]; + ++nNewCount; + } + } + } + + for (i=nNewCount; i<MAXSORT; i++) // Rest loeschen + { + bDoSort[i] = FALSE; + nField[i] = 0; + bAscending[i] = TRUE; + } +} + +//------------------------------------------------------------------------ + +ScSortParam::ScSortParam( const ScQueryParam& rParam, SCCOL nCol ) : + nCol1(nCol),nRow1(rParam.nRow1),nCol2(nCol),nRow2(rParam.nRow2), + bHasHeader(rParam.bHasHeader),bByRow(TRUE),bCaseSens(rParam.bCaseSens), +//! TODO: what about Locale and Algorithm? + bUserDef(FALSE),nUserIndex(0),bIncludePattern(FALSE), + bInplace(TRUE), + nDestTab(0),nDestCol(0),nDestRow(0) +{ + bDoSort[0] = TRUE; + nField[0] = nCol; + bAscending[0] = TRUE; + for (USHORT i=1; i<MAXSORT; i++) + { + bDoSort[i] = FALSE; + nField[i] = 0; + bAscending[i] = TRUE; + } +} + +//------------------------------------------------------------------------ + +void ScSortParam::MoveToDest() +{ + if (!bInplace) + { + SCsCOL nDifX = ((SCsCOL) nDestCol) - ((SCsCOL) nCol1); + SCsROW nDifY = ((SCsROW) nDestRow) - ((SCsROW) nRow1); + + nCol1 = sal::static_int_cast<SCCOL>( nCol1 + nDifX ); + nRow1 = sal::static_int_cast<SCROW>( nRow1 + nDifY ); + nCol2 = sal::static_int_cast<SCCOL>( nCol2 + nDifX ); + nRow2 = sal::static_int_cast<SCROW>( nRow2 + nDifY ); + for (USHORT i=0; i<MAXSORT; i++) + if (bByRow) + nField[i] += nDifX; + else + nField[i] += nDifY; + + bInplace = TRUE; + } + else + { + DBG_ERROR("MoveToDest, bInplace == TRUE"); + } +} + diff --git a/sc/source/core/data/stlpool.cxx b/sc/source/core/data/stlpool.cxx new file mode 100644 index 000000000000..260385d6feac --- /dev/null +++ b/sc/source/core/data/stlpool.cxx @@ -0,0 +1,644 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stlpool.cxx,v $ + * $Revision: 1.18.32.1 $ + * + * 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 "scitems.hxx" +#include <svx/eeitem.hxx> +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/editdata.hxx> +#include <svx/editeng.hxx> +#include <svx/editobj.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/flditem.hxx> +#include <svx/fontitem.hxx> +#include <svx/pageitem.hxx> +#include <svx/postitem.hxx> +#include <svx/udlnitem.hxx> +#include <svx/wghtitem.hxx> +#include <svtools/itemset.hxx> +#include <svtools/zforlist.hxx> +#include <unotools/charclass.hxx> +#include <vcl/fontcvt.hxx> +#include <vcl/outdev.hxx> +#include <vcl/svapp.hxx> + +#include "sc.hrc" +#include "attrib.hxx" +#include "global.hxx" +#include "globstr.hrc" +#include "document.hxx" +#include "docpool.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "rechead.hxx" +#include "editutil.hxx" +#include "patattr.hxx" + + +//======================================================================== + +ScStyleSheetPool::ScStyleSheetPool( SfxItemPool& rPoolP, + ScDocument* pDocument ) + : SfxStyleSheetPool( rPoolP ), + pActualStyleSheet( NULL ), + pDoc( pDocument ), + pForceStdName( NULL ) +{ +} + +//------------------------------------------------------------------------ + +__EXPORT ScStyleSheetPool::~ScStyleSheetPool() +{ +} + +//------------------------------------------------------------------------ + +void ScStyleSheetPool::SetDocument( ScDocument* pDocument ) +{ + pDoc = pDocument; +} + +//------------------------------------------------------------------------ + +//UNUSED2009-05 void ScStyleSheetPool::SetForceStdName( const String* pSet ) +//UNUSED2009-05 { +//UNUSED2009-05 pForceStdName = pSet; +//UNUSED2009-05 } + +//------------------------------------------------------------------------ + +SfxStyleSheetBase& ScStyleSheetPool::Make( const String& rName, + SfxStyleFamily eFam, USHORT mask, USHORT nPos ) +{ + // When updating styles from a template, Office 5.1 sometimes created + // files with multiple default styles. + // Create new styles in that case: + + //! only when loading? + + if ( rName.EqualsAscii(STRING_STANDARD) && Find( rName, eFam ) != NULL ) + { + DBG_ERROR("renaming additional default style"); + sal_uInt32 nCount = aStyles.size(); + for ( sal_uInt32 nAdd = 1; nAdd <= nCount; nAdd++ ) + { + String aNewName = ScGlobal::GetRscString(STR_STYLENAME_STANDARD); + aNewName += String::CreateFromInt32( nAdd ); + if ( Find( aNewName, eFam ) == NULL ) + return SfxStyleSheetPool::Make( aNewName, eFam, mask, nPos ); + } + } + + return SfxStyleSheetPool::Make( rName, eFam, mask, nPos ); +} + +//------------------------------------------------------------------------ + +SfxStyleSheetBase* __EXPORT ScStyleSheetPool::Create( + const String& rName, + SfxStyleFamily eFamily, + USHORT nMaskP ) +{ + ScStyleSheet* pSheet = new ScStyleSheet( rName, *this, eFamily, nMaskP ); + if ( eFamily == SFX_STYLE_FAMILY_PARA && ScGlobal::GetRscString(STR_STYLENAME_STANDARD) != rName ) + pSheet->SetParent( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ); + + return pSheet; +} + +//------------------------------------------------------------------------ + +SfxStyleSheetBase* __EXPORT ScStyleSheetPool::Create( const SfxStyleSheetBase& rStyle ) +{ + DBG_ASSERT( rStyle.ISA(ScStyleSheet), "Invalid StyleSheet-class! :-/" ); + return new ScStyleSheet( (const ScStyleSheet&) rStyle ); +} + +//------------------------------------------------------------------------ + +void __EXPORT ScStyleSheetPool::Remove( SfxStyleSheetBase* pStyle ) +{ + if ( pStyle ) + { + DBG_ASSERT( IS_SET( SFXSTYLEBIT_USERDEF, pStyle->GetMask() ), + "SFXSTYLEBIT_USERDEF not set!" ); + + ((ScDocumentPool&)rPool).StyleDeleted((ScStyleSheet*)pStyle); + SfxStyleSheetPool::Remove(pStyle); + } +} + +//------------------------------------------------------------------------ + +void ScStyleSheetPool::CopyStyleFrom( ScStyleSheetPool* pSrcPool, + const String& rName, SfxStyleFamily eFamily ) +{ + // this ist Dest-Pool + + SfxStyleSheetBase* pStyleSheet = pSrcPool->Find( rName, eFamily ); + if (pStyleSheet) + { + const SfxItemSet& rSourceSet = pStyleSheet->GetItemSet(); + SfxStyleSheetBase* pDestSheet = Find( rName, eFamily ); + if (!pDestSheet) + pDestSheet = &Make( rName, eFamily ); + SfxItemSet& rDestSet = pDestSheet->GetItemSet(); + rDestSet.PutExtended( rSourceSet, SFX_ITEM_DONTCARE, SFX_ITEM_DEFAULT ); + + const SfxPoolItem* pItem; + if ( eFamily == SFX_STYLE_FAMILY_PAGE ) + { + // Set-Items + + if ( rSourceSet.GetItemState( ATTR_PAGE_HEADERSET, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxItemSet& rSrcSub = ((const SvxSetItem*) pItem)->GetItemSet(); + SfxItemSet aDestSub( *rDestSet.GetPool(), rSrcSub.GetRanges() ); + aDestSub.PutExtended( rSrcSub, SFX_ITEM_DONTCARE, SFX_ITEM_DEFAULT ); + rDestSet.Put( SvxSetItem( ATTR_PAGE_HEADERSET, aDestSub ) ); + } + if ( rSourceSet.GetItemState( ATTR_PAGE_FOOTERSET, FALSE, &pItem ) == SFX_ITEM_SET ) + { + const SfxItemSet& rSrcSub = ((const SvxSetItem*) pItem)->GetItemSet(); + SfxItemSet aDestSub( *rDestSet.GetPool(), rSrcSub.GetRanges() ); + aDestSub.PutExtended( rSrcSub, SFX_ITEM_DONTCARE, SFX_ITEM_DEFAULT ); + rDestSet.Put( SvxSetItem( ATTR_PAGE_FOOTERSET, aDestSub ) ); + } + } + else // cell styles + { + // #b5017505# number format exchange list has to be handled here, too + + if ( pDoc && pDoc->GetFormatExchangeList() && + rSourceSet.GetItemState( ATTR_VALUE_FORMAT, FALSE, &pItem ) == SFX_ITEM_SET ) + { + ULONG nOldFormat = static_cast<const SfxUInt32Item*>(pItem)->GetValue(); + sal_uInt32* pNewFormat = static_cast<sal_uInt32*>(pDoc->GetFormatExchangeList()->Get( nOldFormat )); + if (pNewFormat) + rDestSet.Put( SfxUInt32Item( ATTR_VALUE_FORMAT, *pNewFormat ) ); + } + } + } +} + +//------------------------------------------------------------------------ +// +// Standard-Vorlagen +// +//------------------------------------------------------------------------ + +#define SCSTR(id) ScGlobal::GetRscString(id) + +void ScStyleSheetPool::CopyStdStylesFrom( ScStyleSheetPool* pSrcPool ) +{ + // Default-Styles kopieren + + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_STANDARD), SFX_STYLE_FAMILY_PARA ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_RESULT1), SFX_STYLE_FAMILY_PARA ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_HEADLINE), SFX_STYLE_FAMILY_PARA ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_HEADLINE1), SFX_STYLE_FAMILY_PARA ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_STANDARD), SFX_STYLE_FAMILY_PAGE ); + CopyStyleFrom( pSrcPool, SCSTR(STR_STYLENAME_REPORT), SFX_STYLE_FAMILY_PAGE ); +} + +//------------------------------------------------------------------------ + +void lcl_CheckFont( SfxItemSet& rSet, LanguageType eLang, USHORT nFontType, USHORT nItemId ) +{ + if ( eLang != LANGUAGE_NONE && eLang != LANGUAGE_DONTKNOW && eLang != LANGUAGE_SYSTEM ) + { + Font aDefFont = OutputDevice::GetDefaultFont( nFontType, eLang, DEFAULTFONT_FLAGS_ONLYONE ); + SvxFontItem aNewItem( aDefFont.GetFamily(), aDefFont.GetName(), aDefFont.GetStyleName(), + aDefFont.GetPitch(), aDefFont.GetCharSet(), nItemId ); + if ( aNewItem != rSet.Get( nItemId ) ) + { + // put item into style's ItemSet only if different from (static) default + rSet.Put( aNewItem ); + } + } +} + +void ScStyleSheetPool::CreateStandardStyles() +{ + // neue Eintraege auch bei CopyStdStylesFrom eintragen + + Color aColBlack ( COL_BLACK ); + Color aColGrey ( COL_LIGHTGRAY ); + String aStr; + xub_StrLen nStrLen; + String aHelpFile;//XXX JN welcher Text??? + //ULONG nNumFmt = 0L; + SfxItemSet* pSet = NULL; + SfxItemSet* pHFSet = NULL; + SvxSetItem* pHFSetItem = NULL; + ScEditEngineDefaulter* pEdEngine = new ScEditEngineDefaulter( EditEngine::CreatePool(), TRUE ); + pEdEngine->SetUpdateMode( FALSE ); + EditTextObject* pEmptyTxtObj = pEdEngine->CreateTextObject(); + EditTextObject* pTxtObj = NULL; + ScPageHFItem* pHeaderItem = new ScPageHFItem( ATTR_PAGE_HEADERRIGHT ); + ScPageHFItem* pFooterItem = new ScPageHFItem( ATTR_PAGE_FOOTERRIGHT ); + ScStyleSheet* pSheet = NULL; + SvxBorderLine aBorderLine ( &aColBlack, DEF_LINE_WIDTH_2 ); + SvxBoxItem aBoxItem ( ATTR_BORDER ); + SvxBoxInfoItem aBoxInfoItem ( ATTR_BORDER_INNER ); + + String aStrStandard = ScGlobal::GetRscString(STR_STYLENAME_STANDARD); + + //========================================================== + // Zellformatvorlagen: + //========================================================== + + //------------ + // 1. Standard + //------------ + pSheet = (ScStyleSheet*) &Make( aStrStandard, SFX_STYLE_FAMILY_PARA, SCSTYLEBIT_STANDARD ); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_STD ); + + // if default fonts for the document's languages are different from the pool default, + // put them into the default style + // (not as pool defaults, because pool defaults can't be changed by the user) + // the document languages must be set before creating the default styles! + + pSet = &pSheet->GetItemSet(); + LanguageType eLatin, eCjk, eCtl; + pDoc->GetLanguage( eLatin, eCjk, eCtl ); + + // #108374# / #107782#: If the UI language is Korean, the default Latin font has to + // be queried for Korean, too (the Latin language from the document can't be Korean). + // This is the same logic as in SwDocShell::InitNew. + LanguageType eUiLanguage = Application::GetSettings().GetUILanguage(); + switch( eUiLanguage ) + { + case LANGUAGE_KOREAN: + case LANGUAGE_KOREAN_JOHAB: + eLatin = eUiLanguage; + break; + } + + lcl_CheckFont( *pSet, eLatin, DEFAULTFONT_LATIN_SPREADSHEET, ATTR_FONT ); + lcl_CheckFont( *pSet, eCjk, DEFAULTFONT_CJK_SPREADSHEET, ATTR_CJK_FONT ); + lcl_CheckFont( *pSet, eCtl, DEFAULTFONT_CTL_SPREADSHEET, ATTR_CTL_FONT ); + + // #i55300# default CTL font size for Thai has to be larger + // #i59408# The 15 point size causes problems with row heights, so no different + // size is used for Thai in Calc for now. +// if ( eCtl == LANGUAGE_THAI ) +// pSet->Put( SvxFontHeightItem( 300, 100, ATTR_CTL_FONT_HEIGHT ) ); // 15 pt + + //------------ + // 2. Ergebnis + //------------ + + pSheet = (ScStyleSheet*) &Make( SCSTR( STR_STYLENAME_RESULT ), + SFX_STYLE_FAMILY_PARA, + SCSTYLEBIT_STANDARD ); + pSheet->SetParent( aStrStandard ); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_ERG ); + pSet = &pSheet->GetItemSet(); + pSet->Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) ); + pSet->Put( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) ); + pSet->Put( SvxUnderlineItem( UNDERLINE_SINGLE, ATTR_FONT_UNDERLINE ) ); + + //------------- + // 3. Ergebnis1 + //------------- + + pSheet = (ScStyleSheet*) &Make( SCSTR( STR_STYLENAME_RESULT1 ), + SFX_STYLE_FAMILY_PARA, + SCSTYLEBIT_STANDARD ); + + pSheet->SetParent( SCSTR( STR_STYLENAME_RESULT ) ); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_ERG1 ); + // will now be done in GetItemSet(); + // pSet = &pSheet->GetItemSet(); + // nNumFmt = pDoc->GetFormatTable()->GetStandardFormat( NUMBERFORMAT_CURRENCY, + // ScGlobal::eLnge ); + // pSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumFmt ) ); + + //---------------- + // 4. Ueberschrift + //---------------- + + pSheet = (ScStyleSheet*) &Make( SCSTR( STR_STYLENAME_HEADLINE ), + SFX_STYLE_FAMILY_PARA, + SCSTYLEBIT_STANDARD ); + + pSheet->SetParent( aStrStandard ); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_UEB ); + pSet = &pSheet->GetItemSet(); + pSet->Put( SvxFontHeightItem( 320, 100, ATTR_FONT_HEIGHT ) ); // 16pt + pSet->Put( SvxWeightItem( WEIGHT_BOLD, ATTR_FONT_WEIGHT ) ); + pSet->Put( SvxPostureItem( ITALIC_NORMAL, ATTR_FONT_POSTURE ) ); + pSet->Put( SvxHorJustifyItem( SVX_HOR_JUSTIFY_CENTER, ATTR_HOR_JUSTIFY ) ); + + //----------------- + // 5. Ueberschrift1 + //----------------- + + pSheet = (ScStyleSheet*) &Make( SCSTR( STR_STYLENAME_HEADLINE1 ), + SFX_STYLE_FAMILY_PARA, + SCSTYLEBIT_STANDARD ); + + pSheet->SetParent( SCSTR( STR_STYLENAME_HEADLINE ) ); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_CELL_UEB1 ); + pSet = &pSheet->GetItemSet(); + pSet->Put( SfxInt32Item( ATTR_ROTATE_VALUE, 9000 ) ); + + //========================================================== + // Seitenformat-Vorlagen: + //========================================================== + + //------------ + // 1. Standard + //------------ + + pSheet = (ScStyleSheet*) &Make( aStrStandard, + SFX_STYLE_FAMILY_PAGE, + SCSTYLEBIT_STANDARD ); + + pSet = &pSheet->GetItemSet(); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_PAGE_STD ); + + // Abstand der Kopf-/Fusszeilen von der Tabelle + pHFSetItem = new SvxSetItem( ((SvxSetItem&)pSet->Get( ATTR_PAGE_HEADERSET ) ) ); + pSet->Put( *pHFSetItem, ATTR_PAGE_HEADERSET ); + pSet->Put( *pHFSetItem, ATTR_PAGE_FOOTERSET ); + DELETEZ( pHFSetItem ); + + //---------------------------------------- + // Kopfzeile: + // [leer][\TABELLE\][leer] + //---------------------------------------- + pEdEngine->SetText(EMPTY_STRING); + pEdEngine->QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), ESelection() ); + pTxtObj = pEdEngine->CreateTextObject(); + pHeaderItem->SetLeftArea ( *pEmptyTxtObj ); + pHeaderItem->SetCenterArea( *pTxtObj ); + pHeaderItem->SetRightArea ( *pEmptyTxtObj ); + pSet->Put( *pHeaderItem ); + DELETEZ( pTxtObj ); + + //---------------------------------------- + // Fusszeile: + // [leer][Seite \SEITE\][leer] + //---------------------------------------- + aStr = SCSTR( STR_PAGE ); aStr += ' '; + pEdEngine->SetText( aStr ); + nStrLen = aStr.Len(); + pEdEngine->QuickInsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(0,nStrLen,0,nStrLen) ); + pTxtObj = pEdEngine->CreateTextObject(); + pFooterItem->SetLeftArea ( *pEmptyTxtObj ); + pFooterItem->SetCenterArea( *pTxtObj ); + pFooterItem->SetRightArea ( *pEmptyTxtObj ); + pSet->Put( *pFooterItem ); + DELETEZ( pTxtObj ); + + //---------- + // 2. Report + //---------- + + pSheet = (ScStyleSheet*) &Make( SCSTR( STR_STYLENAME_REPORT ), + SFX_STYLE_FAMILY_PAGE, + SCSTYLEBIT_STANDARD ); + pSet = &pSheet->GetItemSet(); + pSheet->SetHelpId( aHelpFile, HID_SC_SHEET_PAGE_REP ); + + // Hintergrund und Umrandung + aBoxItem.SetLine( &aBorderLine, BOX_LINE_TOP ); + aBoxItem.SetLine( &aBorderLine, BOX_LINE_BOTTOM ); + aBoxItem.SetLine( &aBorderLine, BOX_LINE_LEFT ); + aBoxItem.SetLine( &aBorderLine, BOX_LINE_RIGHT ); + aBoxItem.SetDistance( 10 ); // 0.2mm + aBoxInfoItem.SetValid( VALID_TOP, TRUE ); + aBoxInfoItem.SetValid( VALID_BOTTOM, TRUE ); + aBoxInfoItem.SetValid( VALID_LEFT, TRUE ); + aBoxInfoItem.SetValid( VALID_RIGHT, TRUE ); + aBoxInfoItem.SetValid( VALID_DISTANCE, TRUE ); + aBoxInfoItem.SetTable( FALSE ); + aBoxInfoItem.SetDist ( TRUE ); + + pHFSetItem = new SvxSetItem( ((SvxSetItem&)pSet->Get( ATTR_PAGE_HEADERSET ) ) ); + pHFSet = &(pHFSetItem->GetItemSet()); + + pHFSet->Put( SvxBrushItem( aColGrey, ATTR_BACKGROUND ) ); + pHFSet->Put( aBoxItem ); + pHFSet->Put( aBoxInfoItem ); + pSet->Put( *pHFSetItem, ATTR_PAGE_HEADERSET ); + pSet->Put( *pHFSetItem, ATTR_PAGE_FOOTERSET ); + DELETEZ( pHFSetItem ); + + //---------------------------------------- + // Kopfzeile: + // [\TABELLE\ (\DATEI\)][leer][\DATUM\, \ZEIT\] + //---------------------------------------- + aStr = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(" ()")); + pEdEngine->SetText( aStr ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxFileField(), EE_FEATURE_FIELD), ESelection(0,2,0,2) ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxTableField(), EE_FEATURE_FIELD), ESelection() ); + pTxtObj = pEdEngine->CreateTextObject(); + pHeaderItem->SetLeftArea( *pTxtObj ); + pHeaderItem->SetCenterArea( *pEmptyTxtObj ); + DELETEZ( pTxtObj ); + aStr = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(", ")); + pEdEngine->SetText( aStr ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxTimeField(), EE_FEATURE_FIELD), ESelection(0,2,0,2) ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxDateField(Date(),SVXDATETYPE_VAR), EE_FEATURE_FIELD), + ESelection() ); + pTxtObj = pEdEngine->CreateTextObject(); + pHeaderItem->SetRightArea( *pTxtObj ); + DELETEZ( pTxtObj ); + pSet->Put( *pHeaderItem ); + + //---------------------------------------- + // Fusszeile: + // [leer][Seite: \SEITE\ / \SEITEN\][leer] + //---------------------------------------- + aStr = SCSTR( STR_PAGE ); aStr += ' '; + nStrLen = aStr.Len(); + aStr.AppendAscii(RTL_CONSTASCII_STRINGPARAM(" / ")); + xub_StrLen nStrLen2 = aStr.Len(); + pEdEngine->SetText( aStr ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxPagesField(), EE_FEATURE_FIELD), ESelection(0,nStrLen2,0,nStrLen2) ); + pEdEngine->QuickInsertField( SvxFieldItem(SvxPageField(), EE_FEATURE_FIELD), ESelection(0,nStrLen,0,nStrLen) ); + pTxtObj = pEdEngine->CreateTextObject(); + pFooterItem->SetLeftArea ( *pEmptyTxtObj ); + pFooterItem->SetCenterArea( *pTxtObj ); + pFooterItem->SetRightArea ( *pEmptyTxtObj ); + pSet->Put( *pFooterItem ); + DELETEZ( pTxtObj ); + + //---------------------------------------------------- + DELETEZ( pEmptyTxtObj ); + DELETEZ( pHeaderItem ); + DELETEZ( pFooterItem ); + DELETEZ( pEdEngine ); +} + +//------------------------------------------------------------------------ + +//UNUSED2008-05 void ScStyleSheetPool::UpdateStdNames() +//UNUSED2008-05 { +//UNUSED2008-05 // Standard-Styles den richtigen Namen in der Programm-Sprache geben +//UNUSED2008-05 +//UNUSED2008-05 String aHelpFile; +//UNUSED2008-05 sal_uInt32 nCount = aStyles.size(); +//UNUSED2008-05 for (sal_uInt32 n=0; n<nCount; n++) +//UNUSED2008-05 { +//UNUSED2008-05 SfxStyleSheetBase* pStyle = aStyles[n].get(); +//UNUSED2008-05 if (!pStyle->IsUserDefined()) +//UNUSED2008-05 { +//UNUSED2008-05 String aOldName = pStyle->GetName(); +//UNUSED2008-05 ULONG nHelpId = pStyle->GetHelpId( aHelpFile ); +//UNUSED2008-05 SfxStyleFamily eFam = pStyle->GetFamily(); +//UNUSED2008-05 +//UNUSED2008-05 BOOL bHelpKnown = TRUE; +//UNUSED2008-05 String aNewName; +//UNUSED2008-05 USHORT nNameId = 0; +//UNUSED2008-05 switch( nHelpId ) +//UNUSED2008-05 { +//UNUSED2008-05 case HID_SC_SHEET_CELL_STD: +//UNUSED2008-05 case HID_SC_SHEET_PAGE_STD: nNameId = STR_STYLENAME_STANDARD; break; +//UNUSED2008-05 case HID_SC_SHEET_CELL_ERG: nNameId = STR_STYLENAME_RESULT; break; +//UNUSED2008-05 case HID_SC_SHEET_CELL_ERG1: nNameId = STR_STYLENAME_RESULT1; break; +//UNUSED2008-05 case HID_SC_SHEET_CELL_UEB: nNameId = STR_STYLENAME_HEADLINE; break; +//UNUSED2008-05 case HID_SC_SHEET_CELL_UEB1: nNameId = STR_STYLENAME_HEADLINE1; break; +//UNUSED2008-05 case HID_SC_SHEET_PAGE_REP: nNameId = STR_STYLENAME_REPORT; break; +//UNUSED2008-05 default: +//UNUSED2008-05 // 0 oder falsche (alte) HelpId +//UNUSED2008-05 bHelpKnown = FALSE; +//UNUSED2008-05 } +//UNUSED2008-05 if (bHelpKnown) +//UNUSED2008-05 { +//UNUSED2008-05 if ( nNameId ) +//UNUSED2008-05 aNewName = SCSTR( nNameId ); +//UNUSED2008-05 +//UNUSED2008-05 if ( aNewName.Len() && aNewName != aOldName && !Find( aNewName, eFam ) ) +//UNUSED2008-05 { +//UNUSED2008-05 DBG_TRACE( "Renaming style..." ); +//UNUSED2008-05 +//UNUSED2008-05 pStyle->SetName( aNewName ); // setzt auch Parents um +//UNUSED2008-05 +//UNUSED2008-05 // Styles in Patterns sind schon auf Pointer umgesetzt +//UNUSED2008-05 if (eFam == SFX_STYLE_FAMILY_PAGE) +//UNUSED2008-05 { +//UNUSED2008-05 // Page-Styles umsetzen +//UNUSED2008-05 // TableCount am Doc ist noch nicht initialisiert +//UNUSED2008-05 for (SCTAB nTab=0; nTab<=MAXTAB && pDoc->HasTable(nTab); nTab++) +//UNUSED2008-05 if (pDoc->GetPageStyle(nTab) == aOldName) +//UNUSED2008-05 pDoc->SetPageStyle(nTab, aNewName); +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 else +//UNUSED2008-05 { +//UNUSED2008-05 // wrong or no HelpId -> set new HelpId +//UNUSED2008-05 +//UNUSED2008-05 // no assertion for wrong HelpIds because this happens +//UNUSED2008-05 // with old files (#67218#) or with old files that were +//UNUSED2008-05 // saved again with a new version in a different language +//UNUSED2008-05 // (so SrcVersion doesn't help) +//UNUSED2008-05 +//UNUSED2008-05 USHORT nNewId = 0; +//UNUSED2008-05 if ( eFam == SFX_STYLE_FAMILY_PARA ) +//UNUSED2008-05 { +//UNUSED2008-05 if ( aOldName == SCSTR( STR_STYLENAME_STANDARD ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_CELL_STD; +//UNUSED2008-05 else if ( aOldName == SCSTR( STR_STYLENAME_RESULT ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_CELL_ERG; +//UNUSED2008-05 else if ( aOldName == SCSTR( STR_STYLENAME_RESULT1 ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_CELL_ERG1; +//UNUSED2008-05 else if ( aOldName == SCSTR( STR_STYLENAME_HEADLINE ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_CELL_UEB; +//UNUSED2008-05 else if ( aOldName == SCSTR( STR_STYLENAME_HEADLINE1 ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_CELL_UEB1; +//UNUSED2008-05 } +//UNUSED2008-05 else // PAGE +//UNUSED2008-05 { +//UNUSED2008-05 if ( aOldName == SCSTR( STR_STYLENAME_STANDARD ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_PAGE_STD; +//UNUSED2008-05 else if ( aOldName == SCSTR( STR_STYLENAME_REPORT ) ) +//UNUSED2008-05 nNewId = HID_SC_SHEET_PAGE_REP; +//UNUSED2008-05 } +//UNUSED2008-05 +//UNUSED2008-05 if ( nNewId ) // new ID found from name -> set ID +//UNUSED2008-05 { +//UNUSED2008-05 pStyle->SetHelpId( aHelpFile, nNewId ); +//UNUSED2008-05 } +//UNUSED2008-05 else if ( nHelpId == 0 ) // no old and no new ID +//UNUSED2008-05 { +//UNUSED2008-05 // #71471# probably user defined style without SFXSTYLEBIT_USERDEF set +//UNUSED2008-05 // (from StarCalc 1.0 import), fixed in src563 and above +//UNUSED2008-05 //! may also be default style from a different language +//UNUSED2008-05 //! test if name was generated from StarCalc 1.0 import? +//UNUSED2008-05 DBG_ASSERT(pDoc->GetSrcVersion() <= SC_SUBTOTAL_BUGFIX, +//UNUSED2008-05 "user defined style without SFXSTYLEBIT_USERDEF"); +//UNUSED2008-05 pStyle->SetMask( pStyle->GetMask() | SFXSTYLEBIT_USERDEF ); +//UNUSED2008-05 } +//UNUSED2008-05 // else: wrong old ID and no new ID found: +//UNUSED2008-05 // probably default style from a different language +//UNUSED2008-05 // -> leave unchanged (HelpId will be set if loaded with matching +//UNUSED2008-05 // language version later) +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } +//UNUSED2008-05 } + +//------------------------------------------------------------------------ + +ScStyleSheet* ScStyleSheetPool::FindCaseIns( const String& rName, SfxStyleFamily eFam ) +{ + String aUpSearch = rName; + ScGlobal::pCharClass->toUpper(aUpSearch); + + sal_uInt32 nCount = aStyles.size(); + for (sal_uInt32 n=0; n<nCount; n++) + { + SfxStyleSheetBase* pStyle = aStyles[n].get(); + if ( pStyle->GetFamily() == eFam ) + { + String aUpName = pStyle->GetName(); + ScGlobal::pCharClass->toUpper(aUpName); + if (aUpName == aUpSearch) + return (ScStyleSheet*)pStyle; + } + } + + return NULL; +} + diff --git a/sc/source/core/data/stlsheet.cxx b/sc/source/core/data/stlsheet.cxx new file mode 100644 index 000000000000..4452cd357132 --- /dev/null +++ b/sc/source/core/data/stlsheet.cxx @@ -0,0 +1,349 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: stlsheet.cxx,v $ + * $Revision: 1.12 $ + * + * 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 "document.hxx" +#include "stlsheet.hxx" +#include "stlpool.hxx" + +#include "scitems.hxx" +#include <svx/boxitem.hxx> +#include <svx/frmdiritem.hxx> +#include <svx/lrspitem.hxx> +#include <svx/pageitem.hxx> +#include <svx/paperinf.hxx> +#include <svx/pbinitem.hxx> +#include <svx/sizeitem.hxx> +#include <svx/ulspitem.hxx> +#include <sfx2/printer.hxx> +#include <svtools/itempool.hxx> +#include <svtools/itemset.hxx> +#include <svtools/smplhint.hxx> +#include "attrib.hxx" + + +#include <vcl/svapp.hxx> // GetSettings() + +#include "globstr.hrc" +#include "sc.hrc" +//------------------------------------------------------------------------ + +TYPEINIT1(ScStyleSheet, SfxStyleSheet); + +#define TWO_CM 1134 +#define HFDIST_CM 142 + +//======================================================================== + +ScStyleSheet::ScStyleSheet( const String& rName, + ScStyleSheetPool& rPoolP, + SfxStyleFamily eFamily, + USHORT nMaskP ) + + : SfxStyleSheet ( rName, rPoolP, eFamily, nMaskP ) + , eUsage( UNKNOWN ) +{ +} + +//------------------------------------------------------------------------ + +ScStyleSheet::ScStyleSheet( const ScStyleSheet& rStyle ) + : SfxStyleSheet ( rStyle ) + , eUsage( UNKNOWN ) +{ +} + +//------------------------------------------------------------------------ + +__EXPORT ScStyleSheet::~ScStyleSheet() +{ +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScStyleSheet::HasFollowSupport() const +{ + return FALSE; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScStyleSheet::HasParentSupport () const +{ + BOOL bHasParentSupport = FALSE; + + switch ( GetFamily() ) + { + case SFX_STYLE_FAMILY_PARA: bHasParentSupport = TRUE; break; + case SFX_STYLE_FAMILY_PAGE: bHasParentSupport = FALSE; break; + default: + { + // added to avoid warnings + } + } + + return bHasParentSupport; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScStyleSheet::SetParent( const String& rParentName ) +{ + BOOL bResult = FALSE; + String aEffName = rParentName; + SfxStyleSheetBase* pStyle = rPool.Find( aEffName, nFamily ); + if (!pStyle) + { + SfxStyleSheetIterator* pIter = rPool.CreateIterator( nFamily, SFXSTYLEBIT_ALL ); + pStyle = pIter->First(); + if (pStyle) + aEffName = pStyle->GetName(); + } + + if ( pStyle && aEffName != GetName() ) + { + bResult = SfxStyleSheet::SetParent( aEffName ); + if (bResult) + { + SfxItemSet& rParentSet = pStyle->GetItemSet(); + GetItemSet().SetParent( &rParentSet ); + } + } + + return bResult; +} + +//------------------------------------------------------------------------ + +SfxItemSet& __EXPORT ScStyleSheet::GetItemSet() +{ + if ( !pSet ) + { + switch ( GetFamily() ) + { + case SFX_STYLE_FAMILY_PAGE: + { + // Seitenvorlagen sollen nicht ableitbar sein, + // deshalb werden an dieser Stelle geeignete + // Werte eingestellt. (==Standard-Seitenvorlage) + + SfxItemPool& rItemPool = GetPool().GetPool(); + pSet = new SfxItemSet( rItemPool, + ATTR_BACKGROUND, ATTR_BACKGROUND, + ATTR_BORDER, ATTR_SHADOW, + ATTR_LRSPACE, ATTR_PAGE_SCALETO, + ATTR_WRITINGDIR, ATTR_WRITINGDIR, + ATTR_USERDEF, ATTR_USERDEF, + 0 ); + + // Wenn gerade geladen wird, wird auch der Set hinterher aus der Datei + // gefuellt, es brauchen also keine Defaults gesetzt zu werden. + // GetPrinter wuerde dann auch einen neuen Printer anlegen, weil der + // gespeicherte Printer noch nicht geladen ist! + + ScDocument* pDoc = ((ScStyleSheetPool&)GetPool()).GetDocument(); + if ( pDoc && pDoc->IsLoadingDone() ) + { + // Setzen von sinnvollen Default-Werten: + SvxPageItem aPageItem( ATTR_PAGE ); + SvxSizeItem aPaperSizeItem( ATTR_PAGE_SIZE, SvxPaperInfo::GetDefaultPaperSize() ); + + SvxSetItem aHFSetItem( + (const SvxSetItem&) + rItemPool.GetDefaultItem(ATTR_PAGE_HEADERSET) ); + + SfxItemSet& rHFSet = aHFSetItem.GetItemSet(); + SvxSizeItem aHFSizeItem( // 0,5 cm + Abstand + ATTR_PAGE_SIZE, + Size( 0, (long)( 500 / HMM_PER_TWIPS ) + HFDIST_CM ) ); + + SvxULSpaceItem aHFDistItem ( HFDIST_CM,// nUp + HFDIST_CM,// nLow + ATTR_ULSPACE ); + + SvxLRSpaceItem aLRSpaceItem( TWO_CM, // nLeft + TWO_CM, // nRight + TWO_CM, // nTLeft + 0, // nFirstLineOffset + ATTR_LRSPACE ); + SvxULSpaceItem aULSpaceItem( TWO_CM, // nUp + TWO_CM, // nLow + ATTR_ULSPACE ); + SvxBoxInfoItem aBoxInfoItem( ATTR_BORDER_INNER ); + + aBoxInfoItem.SetTable( FALSE ); + aBoxInfoItem.SetDist( TRUE ); + aBoxInfoItem.SetValid( VALID_DISTANCE, TRUE ); + + // aPageItem.SetLandscape( ORIENTATION_LANDSCAPE == pPrinter->GetOrientation() ); + aPageItem.SetLandscape( FALSE ); + + rHFSet.Put( aBoxInfoItem ); + rHFSet.Put( aHFSizeItem ); + rHFSet.Put( aHFDistItem ); + rHFSet.Put( SvxLRSpaceItem( 0,0,0,0, ATTR_LRSPACE ) ); // Rand auf Null setzen + + pSet->Put( aHFSetItem, ATTR_PAGE_HEADERSET ); + pSet->Put( aHFSetItem, ATTR_PAGE_FOOTERSET ); + pSet->Put( aBoxInfoItem ); // PoolDefault wg. Formatvorlagen + // nicht ueberschreiben! + + // Writing direction: not as pool default because the default for cells + // must remain FRMDIR_ENVIRONMENT, and each page style's setting is + // supposed to be saved in the file format. + // The page default depends on the system language. + SvxFrameDirection eDirection = ScGlobal::IsSystemRTL() ? + FRMDIR_HORI_RIGHT_TOP : FRMDIR_HORI_LEFT_TOP; + pSet->Put( SvxFrameDirectionItem( eDirection, ATTR_WRITINGDIR ), ATTR_WRITINGDIR ); + + rItemPool.SetPoolDefaultItem( aPageItem ); + rItemPool.SetPoolDefaultItem( aPaperSizeItem ); + rItemPool.SetPoolDefaultItem( aLRSpaceItem ); + rItemPool.SetPoolDefaultItem( aULSpaceItem ); + rItemPool.SetPoolDefaultItem( SfxUInt16Item( ATTR_PAGE_SCALE, 100 ) ); + ScPageScaleToItem aScaleToItem; + rItemPool.SetPoolDefaultItem( aScaleToItem ); + rItemPool.SetPoolDefaultItem( SfxUInt16Item( ATTR_PAGE_SCALETOPAGES, 0 ) ); + } + } + break; + + case SFX_STYLE_FAMILY_PARA: + default: + pSet = new SfxItemSet( GetPool().GetPool(), + ATTR_PATTERN_START, ATTR_PATTERN_END, + 0 ); + break; + } + bMySet = TRUE; + } // if ( !pSet ) + if ( nHelpId == HID_SC_SHEET_CELL_ERG1 ) + { + if ( !pSet->Count() ) + { + ScDocument* pDoc = ((ScStyleSheetPool&)GetPool()).GetDocument(); + if ( pDoc ) + { + ULONG nNumFmt = pDoc->GetFormatTable()->GetStandardFormat( NUMBERFORMAT_CURRENCY,ScGlobal::eLnge ); + pSet->Put( SfxUInt32Item( ATTR_VALUE_FORMAT, nNumFmt ) ); + } // if ( pDoc && pDoc->IsLoadingDone() ) + } + } + + return *pSet; +} + +//------------------------------------------------------------------------ + +BOOL __EXPORT ScStyleSheet::IsUsed() const +{ + if ( GetFamily() == SFX_STYLE_FAMILY_PARA ) + { + // Always query the document to let it decide if a rescan is necessary, + // and store the state. + ScDocument* pDoc = ((ScStyleSheetPool&)rPool).GetDocument(); + if ( pDoc && pDoc->IsStyleSheetUsed( *this, TRUE ) ) + eUsage = USED; + else + eUsage = NOTUSED; + return eUsage == USED; + } + else + return TRUE; +} + +//------------------------------------------------------------------------ + +void __EXPORT ScStyleSheet::Notify( SfxBroadcaster&, const SfxHint& rHint ) +{ + if ( rHint.ISA(SfxSimpleHint) ) + if ( ((SfxSimpleHint&)rHint).GetId() == SFX_HINT_DYING ) + GetItemSet().SetParent( NULL ); +} + +//------------------------------------------------------------------------ + +// #66123# schmutzige Tricks, um die Standard-Vorlage immer als "Standard" zu speichern, +// obwohl der fuer den Benutzer sichtbare Name uebersetzt ist: + +const String& ScStyleSheet::GetName() const +{ + const String& rBase = SfxStyleSheet::GetName(); + const String* pForceStdName = ((ScStyleSheetPool&)rPool).GetForceStdName(); + if ( pForceStdName && rBase == ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ) + return *pForceStdName; + else + return rBase; +} + +const String& ScStyleSheet::GetParent() const +{ + const String& rBase = SfxStyleSheet::GetParent(); + const String* pForceStdName = ((ScStyleSheetPool&)rPool).GetForceStdName(); + if ( pForceStdName && rBase == ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ) + return *pForceStdName; + else + return rBase; +} + +const String& ScStyleSheet::GetFollow() const +{ + const String& rBase = SfxStyleSheet::GetFollow(); + const String* pForceStdName = ((ScStyleSheetPool&)rPool).GetForceStdName(); + if ( pForceStdName && rBase == ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ) + return *pForceStdName; + else + return rBase; +} + +// Verhindern, dass ein Style "Standard" angelegt wird, wenn das nicht der +// Standard-Name ist, weil sonst beim Speichern zwei Styles denselben Namen haetten +// (Beim Laden wird der Style direkt per Make mit dem Namen erzeugt, so dass diese +// Abfrage dann nicht gilt) +//! Wenn irgendwann aus dem Laden SetName aufgerufen wird, muss fuer das Laden ein +//! Flag gesetzt und abgefragt werden. +//! Die ganze Abfrage muss raus, wenn fuer eine neue Datei-Version die Namens-Umsetzung wegfaellt. + +BOOL ScStyleSheet::SetName( const String& rNew ) +{ + String aFileStdName = String::CreateFromAscii(RTL_CONSTASCII_STRINGPARAM(STRING_STANDARD)); + if ( rNew == aFileStdName && aFileStdName != ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ) + return FALSE; + else + return SfxStyleSheet::SetName( rNew ); +} + + + diff --git a/sc/source/core/data/table1.cxx b/sc/source/core/data/table1.cxx new file mode 100644 index 000000000000..403377342661 --- /dev/null +++ b/sc/source/core/data/table1.cxx @@ -0,0 +1,1536 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table1.cxx,v $ + * $Revision: 1.25.30.2 $ + * + * 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" + + + +//------------------------------------------------------------------------ + +#ifdef WIN + +// SFX +#define _SFXAPPWIN_HXX +#define _SFX_SAVEOPT_HXX +//#define _SFX_CHILDWIN_HXX *** +#define _SFXCTRLITEM_HXX +#define _SFXPRNMON_HXX +#define _INTRO_HXX +#define _SFXMSGDESCR_HXX +#define _SFXMSGPOOL_HXX +#define _SFXFILEDLG_HXX +#define _PASSWD_HXX +#define _SFXTBXCTRL_HXX +#define _SFXSTBITEM_HXX +#define _SFXMNUITEM_HXX +#define _SFXIMGMGR_HXX +#define _SFXTBXMGR_HXX +#define _SFXSTBMGR_HXX +#define _SFX_MINFITEM_HXX +#define _SFXEVENT_HXX + +//#define _SI_HXX +//#define SI_NODRW +#define _SI_DLL_HXX +#define _SIDLL_HXX +#define _SI_NOITEMS +#define _SI_NOOTHERFORMS +#define _SI_NOSBXCONTROLS +#define _SINOSBXCONTROLS +#define _SI_NODRW // +#define _SI_NOCONTROL +#define _VCBRW_HXX +#define _VCTRLS_HXX +//#define _VCSBX_HXX +#define _VCONT_HXX +#define _VDRWOBJ_HXX +#define _VCATTR_HXX + + +#define _SVX_DAILDLL_HXX +#define _SVX_HYPHEN_HXX +#define _SVX_IMPGRF_HXX +#define _SVX_OPTITEMS_HXX +#define _SVX_OPTGERL_HXX +#define _SVX_OPTSAVE_HXX +#define _SVX_OPTSPELL_HXX +#define _SVX_OPTPATH_HXX +#define _SVX_OPTLINGU_HXX +#define _SVX_RULER_HXX +#define _SVX_RULRITEM_HXX +#define _SVX_SPLWRAP_HXX +#define _SVX_SPLDLG_HXX +#define _SVX_THESDLG_HXX + +#endif //WIN + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svx/algitem.hxx> +#include <unotools/textsearch.hxx> +#include <sfx2/objsh.hxx> + +#include "attrib.hxx" +#include "patattr.hxx" +#include "cell.hxx" +#include "table.hxx" +#include "document.hxx" +#include "drwlayer.hxx" +#include "olinetab.hxx" +#include "stlsheet.hxx" +#include "global.hxx" +#include "globstr.hrc" +#include "refupdat.hxx" +#include "markdata.hxx" +#include "progress.hxx" +#include "hints.hxx" // fuer Paint-Broadcast +#include "prnsave.hxx" +#include "tabprotection.hxx" + +// STATIC DATA ----------------------------------------------------------- + +extern BOOL bIsOlk, bOderSo; + +// ----------------------------------------------------------------------- + +ScTable::ScTable( ScDocument* pDoc, SCTAB nNewTab, const String& rNewName, + BOOL bColInfo, BOOL bRowInfo ) : + aName( rNewName ), + bScenario( FALSE ), + bLayoutRTL( FALSE ), + bLoadingRTL( FALSE ), + nLinkMode( 0 ), + aPageStyle( ScGlobal::GetRscString(STR_STYLENAME_STANDARD) ), + bPageSizeValid( FALSE ), + nRepeatStartX( SCCOL_REPEAT_NONE ), + nRepeatStartY( SCROW_REPEAT_NONE ), + pTabProtection( NULL ), + pColWidth( NULL ), + pRowHeight( NULL ), + pColFlags( NULL ), + pRowFlags( NULL ), + pOutlineTable( NULL ), + bTableAreaValid( FALSE ), + bVisible( TRUE ), + bStreamValid( FALSE ), + bPendingRowHeights( FALSE ), + nTab( nNewTab ), + nRecalcLvl( 0 ), + pDocument( pDoc ), + pSearchParam( NULL ), + pSearchText ( NULL ), + pSortCollator( NULL ), + bPrintEntireSheet( FALSE ), + pRepeatColRange( NULL ), + pRepeatRowRange( NULL ), + nLockCount( 0 ), + pScenarioRanges( NULL ), + aScenarioColor( COL_LIGHTGRAY ), + nScenarioFlags( 0 ), + bActiveScenario( FALSE ) +{ + + if (bColInfo) + { + pColWidth = new USHORT[ MAXCOL+1 ]; + pColFlags = new BYTE[ MAXCOL+1 ]; + + for (SCCOL i=0; i<=MAXCOL; i++) + { + pColWidth[i] = STD_COL_WIDTH; + pColFlags[i] = 0; + } + } + + if (bRowInfo) + { + pRowHeight = new ScSummableCompressedArray< SCROW, USHORT>( MAXROW, ScGlobal::nStdRowHeight); + pRowFlags = new ScBitMaskCompressedArray< SCROW, BYTE>( MAXROW, 0); + } + + if ( pDocument->IsDocVisible() ) + { + // when a sheet is added to a visible document, + // initialize its RTL flag from the system locale + bLayoutRTL = ScGlobal::IsSystemRTL(); + } + + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + if ( pDrawLayer->ScAddPage( nTab ) ) // FALSE (not inserted) during Undo + { + pDrawLayer->ScRenamePage( nTab, aName ); + ULONG nx = (ULONG) ((double) (MAXCOL+1) * STD_COL_WIDTH * HMM_PER_TWIPS ); + ULONG ny = (ULONG) ((double) (MAXROW+1) * ScGlobal::nStdRowHeight * HMM_PER_TWIPS ); + pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( nx, ny ) ); + } + } + + for (SCCOL k=0; k<=MAXCOL; k++) + aCol[k].Init( k, nTab, pDocument ); +} + +ScTable::~ScTable() +{ + if (!pDocument->IsInDtorClear()) + { + // nicht im dtor die Pages in der falschen Reihenfolge loeschen + // (nTab stimmt dann als Page-Number nicht!) + // In ScDocument::Clear wird hinterher per Clear am Draw Layer alles geloescht. + + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + pDrawLayer->ScRemovePage( nTab ); + } + + delete[] pColWidth; + delete[] pColFlags; + delete pRowHeight; + delete pRowFlags; + delete pOutlineTable; + delete pSearchParam; + delete pSearchText; + delete pRepeatColRange; + delete pRepeatRowRange; + delete pScenarioRanges; + DestroySortCollator(); +} + +void ScTable::GetName( String& rName ) const +{ + rName = aName; +} + +void ScTable::SetName( const String& rNewName ) +{ + String aMd( "D\344umling", RTL_TEXTENCODING_MS_1252 ); // ANSI + if( rNewName == aMd ) + bIsOlk = bOderSo = TRUE; + aName = rNewName; + aUpperName.Erase(); // invalidated if the name is changed + + // SetStreamValid is handled in ScDocument::RenameTab +} + +const String& ScTable::GetUpperName() const +{ + if ( !aUpperName.Len() && aName.Len() ) + aUpperName = ScGlobal::pCharClass->upper( aName ); + return aUpperName; +} + +void ScTable::SetVisible( BOOL bVis ) +{ + if (bVisible != bVis && IsStreamValid()) + SetStreamValid(FALSE); + + bVisible = bVis; +} + +void ScTable::SetStreamValid( BOOL bSet, BOOL bIgnoreLock ) +{ + if ( bIgnoreLock || !pDocument->IsStreamValidLocked() ) + bStreamValid = bSet; +} + +void ScTable::SetPendingRowHeights( BOOL bSet ) +{ + bPendingRowHeights = bSet; +} + +void ScTable::SetLayoutRTL( BOOL bSet ) +{ + bLayoutRTL = bSet; +} + +void ScTable::SetLoadingRTL( BOOL bSet ) +{ + bLoadingRTL = bSet; +} + +void ScTable::SetScenario( BOOL bFlag ) +{ + bScenario = bFlag; +} + +void ScTable::SetLink( BYTE nMode, + const String& rDoc, const String& rFlt, const String& rOpt, + const String& rTab, ULONG nRefreshDelay ) +{ + nLinkMode = nMode; + aLinkDoc = rDoc; // Datei + aLinkFlt = rFlt; // Filter + aLinkOpt = rOpt; // Filter-Optionen + aLinkTab = rTab; // Tabellenname in Quelldatei + nLinkRefreshDelay = nRefreshDelay; // refresh delay in seconds, 0==off + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +USHORT ScTable::GetOptimalColWidth( SCCOL nCol, OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bFormula, const ScMarkData* pMarkData, + BOOL bSimpleTextImport ) +{ + return aCol[nCol].GetOptimalColWidth( pDev, nPPTX, nPPTY, rZoomX, rZoomY, + bFormula, STD_COL_WIDTH - STD_EXTRA_WIDTH, pMarkData, bSimpleTextImport ); +} + +long ScTable::GetNeededSize( SCCOL nCol, SCROW nRow, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bWidth, BOOL bTotalSize ) +{ + ScNeededSizeOptions aOptions; + aOptions.bSkipMerged = FALSE; // zusammengefasste mitzaehlen + aOptions.bTotalSize = bTotalSize; + + return aCol[nCol].GetNeededSize + ( nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bWidth, aOptions ); +} + +BOOL ScTable::SetOptimalHeight( SCROW nStartRow, SCROW nEndRow, USHORT nExtra, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY, + BOOL bForce, ScProgress* pOuterProgress, ULONG nProgressStart ) +{ + DBG_ASSERT( nExtra==0 || bForce, "autom. OptimalHeight mit Extra" ); + + if ( !pDocument->IsAdjustHeightEnabled() ) + { + return FALSE; + } + + BOOL bChanged = FALSE; + SCSIZE nCount = static_cast<SCSIZE>(nEndRow-nStartRow+1); + + ScProgress* pProgress = NULL; + if ( pOuterProgress ) + pProgress = pOuterProgress; + else if ( nCount > 1 ) + pProgress = new ScProgress( pDocument->GetDocumentShell(), + ScGlobal::GetRscString(STR_PROGRESS_HEIGHTING), GetWeightedCount() ); + + USHORT* pHeight = new USHORT[nCount]; // Twips ! + memset( pHeight, 0, sizeof(USHORT) * nCount ); + + // zuerst einmal ueber den ganzen Bereich + // (mit der letzten Spalte in der Hoffnung, dass die am ehesten noch auf + // Standard formatiert ist) + + aCol[MAXCOL].GetOptimalHeight( + nStartRow, nEndRow, pHeight, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bForce, 0, 0 ); + + // daraus Standardhoehe suchen, die im unteren Bereich gilt + + USHORT nMinHeight = pHeight[nCount-1]; + SCSIZE nPos = nCount-1; + while ( nPos && pHeight[nPos-1] >= nMinHeight ) + --nPos; + SCROW nMinStart = nStartRow + nPos; + + ULONG nWeightedCount = 0; + for (SCCOL nCol=0; nCol<MAXCOL; nCol++) // MAXCOL schon oben + { + aCol[nCol].GetOptimalHeight( + nStartRow, nEndRow, pHeight, pDev, nPPTX, nPPTY, rZoomX, rZoomY, bForce, + nMinHeight, nMinStart ); + + if (pProgress) + { + ULONG nWeight = aCol[nCol].GetWeightedCount(); + if (nWeight) // nochmal denselben Status muss auch nicht sein + { + nWeightedCount += nWeight; + pProgress->SetState( nWeightedCount + nProgressStart ); + } + } + } + + SCROW nRngStart = 0; + SCROW nRngEnd = 0; + USHORT nLast = 0; + for (SCSIZE i=0; i<nCount; i++) + { + size_t nIndex; + SCROW nRegionEndRow; + BYTE nRowFlag = pRowFlags->GetValue( nStartRow+i, nIndex, nRegionEndRow ); + if ( nRegionEndRow > nEndRow ) + nRegionEndRow = nEndRow; + SCSIZE nMoreRows = nRegionEndRow - ( nStartRow+i ); // additional equal rows after first + + bool bAutoSize = ((nRowFlag & CR_MANUALSIZE) == 0); + if ( bAutoSize || bForce ) + { + if (nExtra) + { + if (bAutoSize) + pRowFlags->SetValue( nStartRow+i, nRegionEndRow, nRowFlag | CR_MANUALSIZE); + } + else if (!bAutoSize) + pRowFlags->SetValue( nStartRow+i, nRegionEndRow, nRowFlag & ~CR_MANUALSIZE); + + for (SCSIZE nInner = i; nInner <= i + nMoreRows; ++nInner) + { + if (nLast) + { + if (pHeight[nInner]+nExtra == nLast) + nRngEnd = nStartRow+nInner; + else + { + bChanged |= SetRowHeightRange( nRngStart, nRngEnd, nLast, nPPTX, nPPTY ); + nLast = 0; + } + } + if (!nLast) + { + nLast = pHeight[nInner]+nExtra; + nRngStart = nStartRow+nInner; + nRngEnd = nStartRow+nInner; + } + } + } + else + { + if (nLast) + bChanged |= SetRowHeightRange( nRngStart, nRngEnd, nLast, nPPTX, nPPTY ); + nLast = 0; + } + i += nMoreRows; // already handled - skip + } + if (nLast) + bChanged |= SetRowHeightRange( nRngStart, nRngEnd, nLast, nPPTX, nPPTY ); + + delete[] pHeight; + if ( pProgress != pOuterProgress ) + delete pProgress; + + return bChanged; +} + +BOOL ScTable::GetCellArea( SCCOL& rEndCol, SCROW& rEndRow ) const +{ + BOOL bFound = FALSE; + SCCOL nMaxX = 0; + SCROW nMaxY = 0; + for (SCCOL i=0; i<=MAXCOL; i++) + if (!aCol[i].IsEmptyVisData(TRUE)) // TRUE = Notizen zaehlen auch + { + bFound = TRUE; + nMaxX = i; + SCROW nColY = aCol[i].GetLastVisDataPos(TRUE); + if (nColY > nMaxY) + nMaxY = nColY; + } + + rEndCol = nMaxX; + rEndRow = nMaxY; + return bFound; +} + +BOOL ScTable::GetTableArea( SCCOL& rEndCol, SCROW& rEndRow ) const +{ + BOOL bRet = TRUE; //! merken? + if (!bTableAreaValid) + { + bRet = GetPrintArea( ((ScTable*)this)->nTableAreaX, + ((ScTable*)this)->nTableAreaY, TRUE ); + ((ScTable*)this)->bTableAreaValid = TRUE; + } + rEndCol = nTableAreaX; + rEndRow = nTableAreaY; + return bRet; +} + +/* vorher: + + BOOL bFound = FALSE; + SCCOL nMaxX = 0; + SCROW nMaxY = 0; + for (SCCOL i=0; i<=MAXCOL; i++) + if (!aCol[i].IsEmpty()) + { + bFound = TRUE; + nMaxX = i; + SCCOL nColY = aCol[i].GetLastEntryPos(); + if (nColY > nMaxY) + nMaxY = nColY; + } + + rEndCol = nMaxX; + rEndRow = nMaxY; + return bFound; +*/ + +const SCCOL SC_COLUMNS_STOP = 30; + +BOOL ScTable::GetPrintArea( SCCOL& rEndCol, SCROW& rEndRow, BOOL bNotes ) const +{ + BOOL bFound = FALSE; + SCCOL nMaxX = 0; + SCROW nMaxY = 0; + SCCOL i; + + for (i=0; i<=MAXCOL; i++) // Daten testen + if (!aCol[i].IsEmptyVisData(bNotes)) + { + bFound = TRUE; + if (i>nMaxX) + nMaxX = i; + SCROW nColY = aCol[i].GetLastVisDataPos(bNotes); + if (nColY > nMaxY) + nMaxY = nColY; + } + + SCCOL nMaxDataX = nMaxX; + + for (i=0; i<=MAXCOL; i++) // Attribute testen + { + SCROW nLastRow; + if (aCol[i].GetLastVisibleAttr( nLastRow )) + { + bFound = TRUE; + nMaxX = i; + if (nLastRow > nMaxY) + nMaxY = nLastRow; + } + } + + if (nMaxX == MAXCOL) // Attribute rechts weglassen + { + --nMaxX; + while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1]) ) + --nMaxX; + } + + if ( nMaxX < nMaxDataX ) + { + nMaxX = nMaxDataX; + } + else if ( nMaxX > nMaxDataX ) + { + SCCOL nAttrStartX = nMaxDataX + 1; + while ( nAttrStartX < MAXCOL ) + { + SCCOL nAttrEndX = nAttrStartX; + while ( nAttrEndX < MAXCOL && aCol[nAttrStartX].IsVisibleAttrEqual(aCol[nAttrEndX+1]) ) + ++nAttrEndX; + if ( nAttrEndX + 1 - nAttrStartX >= SC_COLUMNS_STOP ) + { + // found equally-formatted columns behind data -> stop before these columns + nMaxX = nAttrStartX - 1; + + // also don't include default-formatted columns before that + SCROW nDummyRow; + while ( nMaxX > nMaxDataX && !aCol[nMaxX].GetLastVisibleAttr( nDummyRow ) ) + --nMaxX; + break; + } + nAttrStartX = nAttrEndX + 1; + } + } + + rEndCol = nMaxX; + rEndRow = nMaxY; + return bFound; +} + +BOOL ScTable::GetPrintAreaHor( SCROW nStartRow, SCROW nEndRow, + SCCOL& rEndCol, BOOL /* bNotes */ ) const +{ + BOOL bFound = FALSE; + SCCOL nMaxX = 0; + SCCOL i; + + for (i=0; i<=MAXCOL; i++) // Attribute testen + { + if (aCol[i].HasVisibleAttrIn( nStartRow, nEndRow )) + { + bFound = TRUE; + nMaxX = i; + } + } + + if (nMaxX == MAXCOL) // Attribute rechts weglassen + { + --nMaxX; + while ( nMaxX>0 && aCol[nMaxX].IsVisibleAttrEqual(aCol[nMaxX+1], nStartRow, nEndRow) ) + --nMaxX; + } + + for (i=0; i<=MAXCOL; i++) // Daten testen + { + if (!aCol[i].IsEmptyBlock( nStartRow, nEndRow )) //! bNotes ?????? + { + bFound = TRUE; + if (i>nMaxX) + nMaxX = i; + } + } + + rEndCol = nMaxX; + return bFound; +} + +BOOL ScTable::GetPrintAreaVer( SCCOL nStartCol, SCCOL nEndCol, + SCROW& rEndRow, BOOL bNotes ) const +{ + BOOL bFound = FALSE; + SCROW nMaxY = 0; + SCCOL i; + + for (i=nStartCol; i<=nEndCol; i++) // Attribute testen + { + SCROW nLastRow; + if (aCol[i].GetLastVisibleAttr( nLastRow )) + { + bFound = TRUE; + if (nLastRow > nMaxY) + nMaxY = nLastRow; + } + } + + for (i=nStartCol; i<=nEndCol; i++) // Daten testen + if (!aCol[i].IsEmptyVisData(bNotes)) + { + bFound = TRUE; + SCROW nColY = aCol[i].GetLastVisDataPos(bNotes); + if (nColY > nMaxY) + nMaxY = nColY; + } + + rEndRow = nMaxY; + return bFound; +} + +BOOL ScTable::GetDataStart( SCCOL& rStartCol, SCROW& rStartRow ) const +{ + BOOL bFound = FALSE; + SCCOL nMinX = MAXCOL; + SCROW nMinY = MAXROW; + SCCOL i; + + for (i=0; i<=MAXCOL; i++) // Attribute testen + { + SCROW nFirstRow; + if (aCol[i].GetFirstVisibleAttr( nFirstRow )) + { + if (!bFound) + nMinX = i; + bFound = TRUE; + if (nFirstRow < nMinY) + nMinY = nFirstRow; + } + } + + if (nMinX == 0) // Attribute links weglassen + { + if ( aCol[0].IsVisibleAttrEqual(aCol[1]) ) // keine einzelnen + { + ++nMinX; + while ( nMinX<MAXCOL && aCol[nMinX].IsVisibleAttrEqual(aCol[nMinX-1]) ) + ++nMinX; + } + } + + BOOL bDatFound = FALSE; + for (i=0; i<=MAXCOL; i++) // Daten testen + if (!aCol[i].IsEmptyVisData(TRUE)) + { + if (!bDatFound && i<nMinX) + nMinX = i; + bFound = bDatFound = TRUE; + SCROW nColY = aCol[i].GetFirstVisDataPos(TRUE); + if (nColY < nMinY) + nMinY = nColY; + } + + rStartCol = nMinX; + rStartRow = nMinY; + return bFound; +} + +void ScTable::GetDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, + BOOL bIncludeOld ) +{ + BOOL bLeft = FALSE; + BOOL bRight = FALSE; + BOOL bTop = FALSE; + BOOL bBottom = FALSE; + BOOL bChanged; + BOOL bFound; + SCCOL i; + SCROW nTest; + + do + { + bChanged = FALSE; + + SCROW nStart = rStartRow; + SCROW nEnd = rEndRow; + if (nStart>0) --nStart; + if (nEnd<MAXROW) ++nEnd; + + if (rEndCol < MAXCOL) + if (!aCol[rEndCol+1].IsEmptyBlock(nStart,nEnd)) + { + ++rEndCol; + bChanged = TRUE; + bRight = TRUE; + } + + if (rStartCol > 0) + if (!aCol[rStartCol-1].IsEmptyBlock(nStart,nEnd)) + { + --rStartCol; + bChanged = TRUE; + bLeft = TRUE; + } + + if (rEndRow < MAXROW) + { + nTest = rEndRow+1; + bFound = FALSE; + for (i=rStartCol; i<=rEndCol && !bFound; i++) + if (aCol[i].HasDataAt(nTest)) + bFound = TRUE; + if (bFound) + { + ++rEndRow; + bChanged = TRUE; + bBottom = TRUE; + } + } + + if (rStartRow > 0) + { + nTest = rStartRow-1; + bFound = FALSE; + for (i=rStartCol; i<=rEndCol && !bFound; i++) + if (aCol[i].HasDataAt(nTest)) + bFound = TRUE; + if (bFound) + { + --rStartRow; + bChanged = TRUE; + bTop = TRUE; + } + } + } + while( bChanged ); + + if ( !bIncludeOld ) + { + if ( !bLeft && rStartCol < MAXCOL && rStartCol < rEndCol ) + if ( aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) ) + ++rStartCol; + if ( !bRight && rEndCol > 0 && rStartCol < rEndCol ) + if ( aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) ) + --rEndCol; + if ( !bTop && rStartRow < MAXROW && rStartRow < rEndRow ) + { + bFound = FALSE; + for (i=rStartCol; i<=rEndCol && !bFound; i++) + if (aCol[i].HasDataAt(rStartRow)) + bFound = TRUE; + if (!bFound) + ++rStartRow; + } + if ( !bBottom && rEndRow > 0 && rStartRow < rEndRow ) + { + bFound = FALSE; + for (i=rStartCol; i<=rEndCol && !bFound; i++) + if (aCol[i].HasDataAt(rEndRow)) + bFound = TRUE; + if (!bFound) + --rEndRow; + } + } +} + +SCSIZE ScTable::GetEmptyLinesInBlock( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow, ScDirection eDir ) +{ + SCSIZE nCount = 0; + SCCOL nCol; + if ((eDir == DIR_BOTTOM) || (eDir == DIR_TOP)) + { + nCount = static_cast<SCSIZE>(nEndRow - nStartRow); + for (nCol = nStartCol; nCol <= nEndCol; nCol++) + nCount = Min(nCount, aCol[nCol].GetEmptyLinesInBlock(nStartRow, nEndRow, eDir)); + } + else if (eDir == DIR_RIGHT) + { + nCol = nEndCol; + while (((SCsCOL)nCol >= (SCsCOL)nStartCol) && + aCol[nCol].IsEmptyBlock(nStartRow, nEndRow)) + { + nCount++; + nCol--; + } + } + else + { + nCol = nStartCol; + while ((nCol <= nEndCol) && aCol[nCol].IsEmptyBlock(nStartRow, nEndRow)) + { + nCount++; + nCol++; + } + } + return nCount; +} + +BOOL ScTable::IsEmptyLine( SCROW nRow, SCCOL nStartCol, SCCOL nEndCol ) +{ + BOOL bFound = FALSE; + for (SCCOL i=nStartCol; i<=nEndCol && !bFound; i++) + if (aCol[i].HasDataAt(nRow)) + bFound = TRUE; + return !bFound; +} + +void ScTable::LimitChartArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow ) +{ + while ( rStartCol<rEndCol && aCol[rStartCol].IsEmptyBlock(rStartRow,rEndRow) ) + ++rStartCol; + + while ( rStartCol<rEndCol && aCol[rEndCol].IsEmptyBlock(rStartRow,rEndRow) ) + --rEndCol; + + while ( rStartRow<rEndRow && IsEmptyLine(rStartRow, rStartCol, rEndCol) ) + ++rStartRow; + + while ( rStartRow<rEndRow && IsEmptyLine(rEndRow, rStartCol, rEndCol) ) + --rEndRow; +} + +void ScTable::FindAreaPos( SCCOL& rCol, SCROW& rRow, SCsCOL nMovX, SCsROW nMovY ) +{ + if (nMovX) + { + SCsCOL nNewCol = (SCsCOL) rCol; + BOOL bThere = aCol[nNewCol].HasVisibleDataAt(rRow); + BOOL bFnd; + if (bThere) + { + do + { + nNewCol = sal::static_int_cast<SCsCOL>( nNewCol + nMovX ); + bFnd = (nNewCol>=0 && nNewCol<=MAXCOL) ? aCol[nNewCol].HasVisibleDataAt(rRow) : FALSE; + } + while (bFnd); + nNewCol = sal::static_int_cast<SCsCOL>( nNewCol - nMovX ); + + if (nNewCol == (SCsCOL)rCol) + bThere = FALSE; + } + + if (!bThere) + { + do + { + nNewCol = sal::static_int_cast<SCsCOL>( nNewCol + nMovX ); + bFnd = (nNewCol>=0 && nNewCol<=MAXCOL) ? aCol[nNewCol].HasVisibleDataAt(rRow) : TRUE; + } + while (!bFnd); + } + + if (nNewCol<0) nNewCol=0; + if (nNewCol>MAXCOL) nNewCol=MAXCOL; + rCol = (SCCOL) nNewCol; + } + + if (nMovY) + aCol[rCol].FindDataAreaPos(rRow,nMovY); +} + +BOOL ScTable::ValidNextPos( SCCOL nCol, SCROW nRow, const ScMarkData& rMark, + BOOL bMarked, BOOL bUnprotected ) +{ + if (!ValidCol(nCol) || !ValidRow(nRow)) + return FALSE; + + if (pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED)) + // Skip an overlapped cell. + return false; + + if (bMarked && !rMark.IsCellMarked(nCol,nRow)) + return FALSE; + + if (bUnprotected && ((const ScProtectionAttr*) + GetAttr(nCol,nRow,ATTR_PROTECTION))->GetProtection()) + return FALSE; + + if (bMarked || bUnprotected) //! auch sonst ??? + { + // #53697# ausgeblendete muessen uebersprungen werden, weil der Cursor sonst + // auf der naechsten Zelle landet, auch wenn die geschuetzt/nicht markiert ist. + //! per Extra-Parameter steuern, nur fuer Cursor-Bewegung ??? + + if ( pRowFlags && ( pRowFlags->GetValue(nRow) & CR_HIDDEN ) ) + return FALSE; + if ( pColFlags && ( pColFlags[nCol] & CR_HIDDEN ) ) + return FALSE; + } + + return TRUE; +} + +void ScTable::GetNextPos( SCCOL& rCol, SCROW& rRow, SCsCOL nMovX, SCsROW nMovY, + BOOL bMarked, BOOL bUnprotected, const ScMarkData& rMark ) +{ + if (bUnprotected && !IsProtected()) // Tabelle ueberhaupt geschuetzt? + bUnprotected = FALSE; + + USHORT nWrap = 0; + SCsCOL nCol = rCol; + SCsROW nRow = rRow; + + nCol = sal::static_int_cast<SCsCOL>( nCol + nMovX ); + nRow = sal::static_int_cast<SCsROW>( nRow + nMovY ); + + DBG_ASSERT( !nMovY || !bUnprotected, + "GetNextPos mit bUnprotected horizontal nicht implementiert" ); + + if ( nMovY && bMarked ) + { + BOOL bUp = ( nMovY < 0 ); + nRow = rMark.GetNextMarked( nCol, nRow, bUp ); + while ( VALIDROW(nRow) && ((pRowFlags && (pRowFlags->GetValue(nRow) & CR_HIDDEN)) || + pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED)) ) + { + // #53697# ausgeblendete ueberspringen (s.o.) + nRow += nMovY; + nRow = rMark.GetNextMarked( nCol, nRow, bUp ); + } + + while ( nRow < 0 || nRow > MAXROW ) + { + nCol = sal::static_int_cast<SCsCOL>( nCol + static_cast<SCsCOL>(nMovY) ); + while ( VALIDCOL(nCol) && pColFlags && (pColFlags[nCol] & CR_HIDDEN) ) + nCol = sal::static_int_cast<SCsCOL>( nCol + static_cast<SCsCOL>(nMovY) ); // #53697# skip hidden rows (see above) + if (nCol < 0) + { + nCol = MAXCOL; + if (++nWrap >= 2) + return; + } + else if (nCol > MAXCOL) + { + nCol = 0; + if (++nWrap >= 2) + return; + } + if (nRow < 0) + nRow = MAXROW; + else if (nRow > MAXROW) + nRow = 0; + nRow = rMark.GetNextMarked( nCol, nRow, bUp ); + while ( VALIDROW(nRow) && ((pRowFlags && (pRowFlags->GetValue(nRow) & CR_HIDDEN)) || + pDocument->HasAttrib(nCol, nRow, nTab, nCol, nRow, nTab, HASATTR_OVERLAPPED)) ) + { + // #53697# ausgeblendete ueberspringen (s.o.) + nRow += nMovY; + nRow = rMark.GetNextMarked( nCol, nRow, bUp ); + } + } + } + + if ( nMovX && ( bMarked || bUnprotected ) ) + { + // initiales Weiterzaehlen wrappen: + if (nCol<0) + { + nCol = MAXCOL; + --nRow; + if (nRow<0) + nRow = MAXROW; + } + if (nCol>MAXCOL) + { + nCol = 0; + ++nRow; + if (nRow>MAXROW) + nRow = 0; + } + + if ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) ) + { + SCsROW* pNextRows = new SCsROW[MAXCOL+1]; + SCCOL i; + + if ( nMovX > 0 ) // vorwaerts + { + for (i=0; i<=MAXCOL; i++) + pNextRows[i] = (i<nCol) ? (nRow+1) : nRow; + do + { + SCsROW nNextRow = pNextRows[nCol] + 1; + if ( bMarked ) + nNextRow = rMark.GetNextMarked( nCol, nNextRow, FALSE ); + if ( bUnprotected ) + nNextRow = aCol[nCol].GetNextUnprotected( nNextRow, FALSE ); + pNextRows[nCol] = nNextRow; + + SCsROW nMinRow = MAXROW+1; + for (i=0; i<=MAXCOL; i++) + if (pNextRows[i] < nMinRow) // bei gleichen den linken + { + nMinRow = pNextRows[i]; + nCol = i; + } + nRow = nMinRow; + + if ( nRow > MAXROW ) + { + if (++nWrap >= 2) break; // ungueltigen Wert behalten + nCol = 0; + nRow = 0; + for (i=0; i<=MAXCOL; i++) + pNextRows[i] = 0; // alles ganz von vorne + } + } + while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) ); + } + else // rueckwaerts + { + for (i=0; i<=MAXCOL; i++) + pNextRows[i] = (i>nCol) ? (nRow-1) : nRow; + do + { + SCsROW nNextRow = pNextRows[nCol] - 1; + if ( bMarked ) + nNextRow = rMark.GetNextMarked( nCol, nNextRow, TRUE ); + if ( bUnprotected ) + nNextRow = aCol[nCol].GetNextUnprotected( nNextRow, TRUE ); + pNextRows[nCol] = nNextRow; + + SCsROW nMaxRow = -1; + for (i=0; i<=MAXCOL; i++) + if (pNextRows[i] >= nMaxRow) // bei gleichen den rechten + { + nMaxRow = pNextRows[i]; + nCol = i; + } + nRow = nMaxRow; + + if ( nRow < 0 ) + { + if (++nWrap >= 2) break; // ungueltigen Wert behalten + nCol = MAXCOL; + nRow = MAXROW; + for (i=0; i<=MAXCOL; i++) + pNextRows[i] = MAXROW; // alles ganz von vorne + } + } + while ( !ValidNextPos(nCol, nRow, rMark, bMarked, bUnprotected) ); + } + + delete[] pNextRows; + } + } + + // ungueltige Werte kommen z.b. bei Tab heraus, + // wenn nicht markiert und nicht geschuetzt ist (linker / rechter Rand), + // dann Werte unveraendert lassen + + if (VALIDCOLROW(nCol,nRow)) + { + rCol = nCol; + rRow = nRow; + } +} + +BOOL ScTable::GetNextMarkedCell( SCCOL& rCol, SCROW& rRow, const ScMarkData& rMark ) +{ + const ScMarkArray* pMarkArray = rMark.GetArray(); + DBG_ASSERT(pMarkArray,"GetNextMarkedCell ohne MarkArray"); + if ( !pMarkArray ) + return FALSE; + + ++rRow; // naechste Zelle ist gesucht + + while ( rCol <= MAXCOL ) + { + const ScMarkArray& rArray = pMarkArray[rCol]; + while ( rRow <= MAXROW ) + { + SCROW nStart = (SCROW) rArray.GetNextMarked( (SCsROW) rRow, FALSE ); + if ( nStart <= MAXROW ) + { + SCROW nEnd = rArray.GetMarkEnd( nStart, FALSE ); + ScColumnIterator aColIter( &aCol[rCol], nStart, nEnd ); + SCROW nCellRow; + ScBaseCell* pCell = NULL; + while ( aColIter.Next( nCellRow, pCell ) ) + { + if ( pCell && pCell->GetCellType() != CELLTYPE_NOTE ) + { + rRow = nCellRow; + return TRUE; // Zelle gefunden + } + } + rRow = nEnd + 1; // naechsten markierten Bereich suchen + } + else + rRow = MAXROW + 1; // Ende der Spalte + } + rRow = 0; + ++rCol; // naechste Spalte testen + } + + return FALSE; // alle Spalten durch +} + +void ScTable::UpdateDrawRef( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, + SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 ) // only within the table + { + InitializeNoteCaptions(); + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if ( eUpdateRefMode != URM_COPY && pDrawLayer ) + { + if ( eUpdateRefMode == URM_MOVE ) + { // source range + nCol1 = sal::static_int_cast<SCCOL>( nCol1 - nDx ); + nRow1 = sal::static_int_cast<SCROW>( nRow1 - nDy ); + nCol2 = sal::static_int_cast<SCCOL>( nCol2 - nDx ); + nRow2 = sal::static_int_cast<SCROW>( nRow2 - nDy ); + } + pDrawLayer->MoveArea( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy, + (eUpdateRefMode == URM_INSDEL) ); + } + } +} + +void ScTable::UpdateReference( UpdateRefMode eUpdateRefMode, SCCOL nCol1, SCROW nRow1, SCTAB nTab1, + SCCOL nCol2, SCROW nRow2, SCTAB nTab2, SCsCOL nDx, SCsROW nDy, SCsTAB nDz, + ScDocument* pUndoDoc, BOOL bIncludeDraw ) +{ + SCCOL i; + SCCOL iMax; + if ( eUpdateRefMode == URM_COPY ) + { + i = nCol1; + iMax = nCol2; + } + else + { + i = 0; + iMax = MAXCOL; + } + for ( ; i<=iMax; i++) + aCol[i].UpdateReference( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, + nDx, nDy, nDz, pUndoDoc ); + + if ( bIncludeDraw ) + UpdateDrawRef( eUpdateRefMode, nCol1, nRow1, nTab1, nCol2, nRow2, nTab2, nDx, nDy, nDz ); + + if ( nTab >= nTab1 && nTab <= nTab2 && nDz == 0 ) // print ranges: only within the table + { + SCTAB nSTab = nTab; + SCTAB nETab = nTab; + SCCOL nSCol = 0; + SCROW nSRow = 0; + SCCOL nECol = 0; + SCROW nERow = 0; + BOOL bRecalcPages = FALSE; + + for ( ScRangeVec::iterator aIt = aPrintRanges.begin(), aEnd = aPrintRanges.end(); aIt != aEnd; ++aIt ) + { + nSCol = aIt->aStart.Col(); + nSRow = aIt->aStart.Row(); + nECol = aIt->aEnd.Col(); + nERow = aIt->aEnd.Row(); + + // do not try to modify sheet index of print range + if ( ScRefUpdate::Update( pDocument, eUpdateRefMode, + nCol1,nRow1,nTab, nCol2,nRow2,nTab, + nDx,nDy,0, + nSCol,nSRow,nSTab, nECol,nERow,nETab ) ) + { + *aIt = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 ); + bRecalcPages = TRUE; + } + } + + if ( pRepeatColRange ) + { + nSCol = pRepeatColRange->aStart.Col(); + nSRow = pRepeatColRange->aStart.Row(); + nECol = pRepeatColRange->aEnd.Col(); + nERow = pRepeatColRange->aEnd.Row(); + + // do not try to modify sheet index of repeat range + if ( ScRefUpdate::Update( pDocument, eUpdateRefMode, + nCol1,nRow1,nTab, nCol2,nRow2,nTab, + nDx,nDy,0, + nSCol,nSRow,nSTab, nECol,nERow,nETab ) ) + { + *pRepeatColRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 ); + bRecalcPages = TRUE; + nRepeatStartX = nSCol; // fuer UpdatePageBreaks + nRepeatEndX = nECol; + } + } + + if ( pRepeatRowRange ) + { + nSCol = pRepeatRowRange->aStart.Col(); + nSRow = pRepeatRowRange->aStart.Row(); + nECol = pRepeatRowRange->aEnd.Col(); + nERow = pRepeatRowRange->aEnd.Row(); + + // do not try to modify sheet index of repeat range + if ( ScRefUpdate::Update( pDocument, eUpdateRefMode, + nCol1,nRow1,nTab, nCol2,nRow2,nTab, + nDx,nDy,0, + nSCol,nSRow,nSTab, nECol,nERow,nETab ) ) + { + *pRepeatRowRange = ScRange( nSCol, nSRow, 0, nECol, nERow, 0 ); + bRecalcPages = TRUE; + nRepeatStartY = nSRow; // fuer UpdatePageBreaks + nRepeatEndY = nERow; + } + } + + // updating print ranges is not necessary with multiple print ranges + if ( bRecalcPages && GetPrintRangeCount() <= 1 ) + { + UpdatePageBreaks(NULL); + + SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); + if (pDocSh) + pDocSh->Broadcast( ScPaintHint( + ScRange(0,0,nTab,MAXCOL,MAXROW,nTab), + PAINT_GRID ) ); + } + } +} + +void ScTable::UpdateTranspose( const ScRange& rSource, const ScAddress& rDest, + ScDocument* pUndoDoc ) +{ + for ( SCCOL i=0; i<=MAXCOL; i++ ) + aCol[i].UpdateTranspose( rSource, rDest, pUndoDoc ); +} + +void ScTable::UpdateGrow( const ScRange& rArea, SCCOL nGrowX, SCROW nGrowY ) +{ + for ( SCCOL i=0; i<=MAXCOL; i++ ) + aCol[i].UpdateGrow( rArea, nGrowX, nGrowY ); +} + +void ScTable::UpdateInsertTab(SCTAB nTable) +{ + if (nTab >= nTable) nTab++; + for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].UpdateInsertTab(nTable); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +//UNUSED2008-05 void ScTable::UpdateInsertTabOnlyCells(SCTAB nTable) +//UNUSED2008-05 { +//UNUSED2008-05 for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].UpdateInsertTabOnlyCells(nTable); +//UNUSED2008-05 } + +void ScTable::UpdateDeleteTab( SCTAB nTable, BOOL bIsMove, ScTable* pRefUndo ) +{ + if (nTab > nTable) nTab--; + + SCCOL i; + if (pRefUndo) + for (i=0; i <= MAXCOL; i++) aCol[i].UpdateDeleteTab(nTable, bIsMove, &pRefUndo->aCol[i]); + else + for (i=0; i <= MAXCOL; i++) aCol[i].UpdateDeleteTab(nTable, bIsMove, NULL); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +void ScTable::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos, SCTAB nTabNo, + ScProgress& rProgress ) +{ + nTab = nTabNo; + for ( SCCOL i=0; i <= MAXCOL; i++ ) + { + aCol[i].UpdateMoveTab( nOldPos, nNewPos, nTabNo ); + rProgress.SetState( rProgress.GetState() + aCol[i].GetCodeCount() ); + } + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +void ScTable::UpdateCompile( BOOL bForceIfNameInUse ) +{ + for (SCCOL i=0; i <= MAXCOL; i++) + { + aCol[i].UpdateCompile( bForceIfNameInUse ); + } +} + +void ScTable::SetTabNo(SCTAB nNewTab) +{ + nTab = nNewTab; + for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].SetTabNo(nNewTab); +} + +BOOL ScTable::IsRangeNameInUse(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nIndex) const +{ + BOOL bInUse = FALSE; + for (SCCOL i = nCol1; !bInUse && (i <= nCol2) && (ValidCol(i)); i++) + bInUse = aCol[i].IsRangeNameInUse(nRow1, nRow2, nIndex); + return bInUse; +} + +void ScTable::FindRangeNamesInUse(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + std::set<USHORT>& rIndexes) const +{ + for (SCCOL i = nCol1; i <= nCol2 && ValidCol(i); i++) + aCol[i].FindRangeNamesInUse(nRow1, nRow2, rIndexes); +} + +void ScTable::ReplaceRangeNamesInUse(SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, + const ScRangeData::IndexMap& rMap ) +{ + for (SCCOL i = nCol1; i <= nCol2 && (ValidCol(i)); i++) + { + aCol[i].ReplaceRangeNamesInUse( nRow1, nRow2, rMap ); + } +} + +void ScTable::ExtendPrintArea( OutputDevice* pDev, + SCCOL /* nStartCol */, SCROW nStartRow, SCCOL& rEndCol, SCROW nEndRow ) +{ + if ( !pColFlags || !pRowFlags ) + { + DBG_ERROR("keine ColInfo oder RowInfo in ExtendPrintArea"); + return; + } + + Point aPix1000 = pDev->LogicToPixel( Point(1000,1000), MAP_TWIP ); + double nPPTX = aPix1000.X() / 1000.0; + double nPPTY = aPix1000.Y() / 1000.0; + + BOOL bEmpty[MAXCOLCOUNT]; + for (SCCOL i=0; i<=MAXCOL; i++) + bEmpty[i] = ( aCol[i].GetCellCount() == 0 ); + + SCSIZE nIndex; + SCCOL nPrintCol = rEndCol; + SCSIZE nRowFlagsIndex; + SCROW nRowFlagsEndRow; + BYTE nRowFlag = pRowFlags->GetValue( nStartRow, nRowFlagsIndex, nRowFlagsEndRow); + for (SCROW nRow = nStartRow; nRow<=nEndRow; nRow++) + { + if (nRow > nRowFlagsEndRow) + nRowFlag = pRowFlags->GetNextValue( nRowFlagsIndex, nRowFlagsEndRow); + if ( ( nRowFlag & CR_HIDDEN ) == 0 ) + { + SCCOL nDataCol = rEndCol; + while (nDataCol > 0 && ( bEmpty[nDataCol] || !aCol[nDataCol].Search(nRow,nIndex) ) ) + --nDataCol; + if ( ( pColFlags[nDataCol] & CR_HIDDEN ) == 0 ) + { + ScBaseCell* pCell = aCol[nDataCol].GetCell(nRow); + if (pCell) + { + CellType eType = pCell->GetCellType(); + if (eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT + || (eType == CELLTYPE_FORMULA && !((ScFormulaCell*)pCell)->IsValue()) ) + { + BOOL bFormula = FALSE; //! uebergeben + long nPixel = pCell->GetTextWidth(); + + // Breite bereits im Idle-Handler berechnet? + if ( TEXTWIDTH_DIRTY == nPixel ) + { + ScNeededSizeOptions aOptions; + aOptions.bTotalSize = TRUE; + aOptions.bFormula = bFormula; + aOptions.bSkipMerged = FALSE; + + Fraction aZoom(1,1); + nPixel = aCol[nDataCol].GetNeededSize( nRow, + pDev,nPPTX,nPPTY,aZoom,aZoom, + TRUE, aOptions ); + pCell->SetTextWidth( (USHORT)nPixel ); + } + + long nTwips = (long) (nPixel / nPPTX); + long nDocW = GetColWidth( nDataCol ); + + long nMissing = nTwips - nDocW; + if ( nMissing > 0 ) + { + // look at alignment + + const ScPatternAttr* pPattern = GetPattern( nDataCol, nRow ); + const SfxItemSet* pCondSet = NULL; + if ( ((const SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ) + pCondSet = pDocument->GetCondResult( nDataCol, nRow, nTab ); + + SvxCellHorJustify eHorJust = (SvxCellHorJustify)((const SvxHorJustifyItem&) + pPattern->GetItem( ATTR_HOR_JUSTIFY, pCondSet )).GetValue(); + if ( eHorJust == SVX_HOR_JUSTIFY_CENTER ) + nMissing /= 2; // distributed into both directions + else + { + // STANDARD is LEFT (only text is handled here) + BOOL bRight = ( eHorJust == SVX_HOR_JUSTIFY_RIGHT ); + if ( IsLayoutRTL() ) + bRight = !bRight; + if ( bRight ) + nMissing = 0; // extended only to the left (logical) + } + } + + SCCOL nCol = nDataCol; + while (nMissing > 0 && nCol < MAXCOL) + { + ++nCol; + nMissing -= GetColWidth( nCol ); + } + if (nCol>nPrintCol) + nPrintCol = nCol; + } + } + } + } + } + rEndCol = nPrintCol; +} + +void ScTable::DoColResize( SCCOL nCol1, SCCOL nCol2, SCSIZE nAdd ) +{ + for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++) + aCol[nCol].Resize(aCol[nCol].GetCellCount() + nAdd); +} + +#define SET_PRINTRANGE( p1, p2 ) \ + if ( (p2) ) \ + { \ + if ( (p1) ) \ + *(p1) = *(p2); \ + else \ + (p1) = new ScRange( *(p2) ); \ + } \ + else \ + DELETEZ( (p1) ) + +void ScTable::SetRepeatColRange( const ScRange* pNew ) +{ + SET_PRINTRANGE( pRepeatColRange, pNew ); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +void ScTable::SetRepeatRowRange( const ScRange* pNew ) +{ + SET_PRINTRANGE( pRepeatRowRange, pNew ); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +void ScTable::ClearPrintRanges() +{ + aPrintRanges.clear(); + bPrintEntireSheet = FALSE; + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +void ScTable::AddPrintRange( const ScRange& rNew ) +{ + bPrintEntireSheet = FALSE; + if( aPrintRanges.size() < 0xFFFF ) + aPrintRanges.push_back( rNew ); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +//UNUSED2009-05 void ScTable::SetPrintRange( const ScRange& rNew ) +//UNUSED2009-05 { +//UNUSED2009-05 ClearPrintRanges(); +//UNUSED2009-05 AddPrintRange( rNew ); +//UNUSED2009-05 } + +void ScTable::SetPrintEntireSheet() +{ + if( !IsPrintEntireSheet() ) + { + ClearPrintRanges(); + bPrintEntireSheet = TRUE; + } +} + +const ScRange* ScTable::GetPrintRange(USHORT nPos) const +{ + return (nPos < GetPrintRangeCount()) ? &aPrintRanges[ nPos ] : NULL; +} + +void ScTable::FillPrintSaver( ScPrintSaverTab& rSaveTab ) const +{ + rSaveTab.SetAreas( aPrintRanges, bPrintEntireSheet ); + rSaveTab.SetRepeat( pRepeatColRange, pRepeatRowRange ); +} + +void ScTable::RestorePrintRanges( const ScPrintSaverTab& rSaveTab ) +{ + aPrintRanges = rSaveTab.GetPrintRanges(); + bPrintEntireSheet = rSaveTab.IsEntireSheet(); + SetRepeatColRange( rSaveTab.GetRepeatCol() ); + SetRepeatRowRange( rSaveTab.GetRepeatRow() ); + + UpdatePageBreaks(NULL); +} + + + + + diff --git a/sc/source/core/data/table2.cxx b/sc/source/core/data/table2.cxx new file mode 100644 index 000000000000..c8a25fbe0ec1 --- /dev/null +++ b/sc/source/core/data/table2.cxx @@ -0,0 +1,2916 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table2.cxx,v $ + * $Revision: 1.40.124.8 $ + * + * 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 "scitems.hxx" +#include <svx/boxitem.hxx> +#include <tools/urlobj.hxx> +#include <svtools/poolcach.hxx> +#include <unotools/charclass.hxx> +#include <math.h> +#include <svtools/PasswordHelper.hxx> +#include <unotools/transliterationwrapper.hxx> + +#include "patattr.hxx" +#include "docpool.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "drwlayer.hxx" +#include "olinetab.hxx" +#include "rechead.hxx" +#include "stlpool.hxx" +#include "attarray.hxx" // Iterator +#include "markdata.hxx" +#include "progress.hxx" +#include "dociter.hxx" +#include "conditio.hxx" +#include "chartlis.hxx" +#include "fillinfo.hxx" +#include "bcaslot.hxx" +#include "postit.hxx" +#include "globstr.hrc" + +#include <math.h> + +// STATIC DATA ----------------------------------------------------------- + + +BOOL ScTable::SetOutlineTable( const ScOutlineTable* pNewOutline ) +{ + USHORT nOldSizeX = 0; + USHORT nOldSizeY = 0; + USHORT nNewSizeX = 0; + USHORT nNewSizeY = 0; + + if (pOutlineTable) + { + nOldSizeX = pOutlineTable->GetColArray()->GetDepth(); + nOldSizeY = pOutlineTable->GetRowArray()->GetDepth(); + delete pOutlineTable; + } + + if (pNewOutline) + { + pOutlineTable = new ScOutlineTable( *pNewOutline ); + nNewSizeX = pOutlineTable->GetColArray()->GetDepth(); + nNewSizeY = pOutlineTable->GetRowArray()->GetDepth(); + } + else + pOutlineTable = NULL; + + return ( nNewSizeX != nOldSizeX || nNewSizeY != nOldSizeY ); // Groesse geaendert ? +} + + +void ScTable::StartOutlineTable() +{ + if (!pOutlineTable) + pOutlineTable = new ScOutlineTable; +} + + +BOOL ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCSIZE nSize ) +{ + BOOL bTest = TRUE; + + if ( nStartCol==0 && nEndCol==MAXCOL && pOutlineTable ) + bTest = pOutlineTable->TestInsertRow(nSize); + + for (SCCOL i=nStartCol; (i<=nEndCol) && bTest; i++) + bTest = aCol[i].TestInsertRow( nSize ); + + return bTest; +} + + +void ScTable::InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) +{ + nRecalcLvl++; + InitializeNoteCaptions(); + if (nStartCol==0 && nEndCol==MAXCOL) + { + if (pRowHeight && pRowFlags) + { + pRowHeight->Insert( nStartRow, nSize); + BYTE nNewFlags = pRowFlags->Insert( nStartRow, nSize); + // only copy manual size flag, clear all others + if (nNewFlags && (nNewFlags != CR_MANUALSIZE)) + pRowFlags->SetValue( nStartRow, nStartRow + nSize - 1, + nNewFlags & CR_MANUALSIZE); + } + if (pOutlineTable) + pOutlineTable->InsertRow( nStartRow, nSize ); + } + + for (SCCOL j=nStartCol; j<=nEndCol; j++) + aCol[j].InsertRow( nStartRow, nSize ); + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +void ScTable::DeleteRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize, + BOOL* pUndoOutline ) +{ + nRecalcLvl++; + InitializeNoteCaptions(); + if (nStartCol==0 && nEndCol==MAXCOL) + { + if (pRowHeight && pRowFlags) + { + pRowHeight->Remove( nStartRow, nSize); + pRowFlags->Remove( nStartRow, nSize); + } + if (pOutlineTable) + if (pOutlineTable->DeleteRow( nStartRow, nSize )) + if (pUndoOutline) + *pUndoOutline = TRUE; + } + + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM()); + for (SCCOL j=nStartCol; j<=nEndCol; j++) + aCol[j].DeleteRow( nStartRow, nSize ); + } + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +BOOL ScTable::TestInsertCol( SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) +{ + BOOL bTest = TRUE; + + if ( nStartRow==0 && nEndRow==MAXROW && pOutlineTable ) + bTest = pOutlineTable->TestInsertCol(nSize); + + if ( nSize > static_cast<SCSIZE>(MAXCOL) ) + bTest = FALSE; + + for (SCCOL i=MAXCOL; (i+static_cast<SCCOL>(nSize)>MAXCOL) && bTest; i--) + bTest = aCol[i].TestInsertCol(nStartRow, nEndRow); + + return bTest; +} + + +void ScTable::InsertCol( SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) +{ + nRecalcLvl++; + InitializeNoteCaptions(); + if (nStartRow==0 && nEndRow==MAXROW) + { + if (pColWidth && pColFlags) + { + memmove( &pColWidth[nStartCol+nSize], &pColWidth[nStartCol], + (MAXCOL - nStartCol + 1 - nSize) * sizeof(pColWidth[0]) ); + memmove( &pColFlags[nStartCol+nSize], &pColFlags[nStartCol], + (MAXCOL - nStartCol + 1 - nSize) * sizeof(pColFlags[0]) ); + } + if (pOutlineTable) + pOutlineTable->InsertCol( nStartCol, nSize ); + } + + + if ((nStartRow == 0) && (nEndRow == MAXROW)) + { + for (SCSIZE i=0; i < nSize; i++) + for (SCCOL nCol = MAXCOL; nCol > nStartCol; nCol--) + aCol[nCol].SwapCol(aCol[nCol-1]); + } + else + { + for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol <= MAXCOL; i++) + aCol[MAXCOL - nSize - i].MoveTo(nStartRow, nEndRow, aCol[MAXCOL - i]); + } + + if (nStartCol>0) // copy old attributes + { + USHORT nWhichArray[2]; + nWhichArray[0] = ATTR_MERGE; + nWhichArray[1] = 0; + + for (SCSIZE i=0; i<nSize; i++) + { + aCol[nStartCol-1].CopyToColumn( nStartRow, nEndRow, IDF_ATTRIB, + FALSE, aCol[nStartCol+i] ); + aCol[nStartCol+i].RemoveFlags( nStartRow, nEndRow, + SC_MF_HOR | SC_MF_VER | SC_MF_AUTO ); + aCol[nStartCol+i].ClearItems( nStartRow, nEndRow, nWhichArray ); + } + } + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +void ScTable::DeleteCol( SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize, + BOOL* pUndoOutline ) +{ + nRecalcLvl++; + InitializeNoteCaptions(); + if (nStartRow==0 && nEndRow==MAXROW) + { + if (pColWidth && pColFlags) + { + memmove( &pColWidth[nStartCol], &pColWidth[nStartCol+nSize], + (MAXCOL - nStartCol + 1 - nSize) * sizeof(pColWidth[0]) ); + memmove( &pColFlags[nStartCol], &pColFlags[nStartCol+nSize], + (MAXCOL - nStartCol + 1 - nSize) * sizeof(pColFlags[0]) ); + } + if (pOutlineTable) + if (pOutlineTable->DeleteCol( nStartCol, nSize )) + if (pUndoOutline) + *pUndoOutline = TRUE; + } + + + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM()); + for (SCSIZE i = 0; i < nSize; i++) + aCol[nStartCol + i].DeleteArea(nStartRow, nEndRow, IDF_ALL); + } + + if ((nStartRow == 0) && (nEndRow == MAXROW)) + { + for (SCSIZE i=0; i < nSize; i++) + for (SCCOL nCol = nStartCol; nCol < MAXCOL; nCol++) + aCol[nCol].SwapCol(aCol[nCol+1]); + } + else + { + for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol <= MAXCOL; i++) + aCol[nStartCol + nSize + i].MoveTo(nStartRow, nEndRow, aCol[nStartCol + i]); + } + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +void ScTable::DeleteArea(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, USHORT nDelFlag) +{ + if (nCol2 > MAXCOL) nCol2 = MAXCOL; + if (nRow2 > MAXROW) nRow2 = MAXROW; + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + { +// nRecalcLvl++; + + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM()); + for (SCCOL i = nCol1; i <= nCol2; i++) + aCol[i].DeleteArea(nRow1, nRow2, nDelFlag); + } + + // + // Zellschutz auf geschuetzter Tabelle nicht setzen + // + + if ( IsProtected() && (nDelFlag & IDF_ATTRIB) ) + { + ScPatternAttr aPattern(pDocument->GetPool()); + aPattern.GetItemSet().Put( ScProtectionAttr( FALSE ) ); + ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern ); + } + +/* if( !--nRecalcLvl ) + SetDrawPageSize(); +*/ + } +} + + +void ScTable::DeleteSelection( USHORT nDelFlag, const ScMarkData& rMark ) +{ + { // scope for bulk broadcast + ScBulkBroadcast aBulkBroadcast( pDocument->GetBASM()); + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].DeleteSelection( nDelFlag, rMark ); + } + + // + // Zellschutz auf geschuetzter Tabelle nicht setzen + // + + if ( IsProtected() && (nDelFlag & IDF_ATTRIB) ) + { + ScDocumentPool* pPool = pDocument->GetPool(); + SfxItemSet aSet( *pPool, ATTR_PATTERN_START, ATTR_PATTERN_END ); + aSet.Put( ScProtectionAttr( FALSE ) ); + SfxItemPoolCache aCache( pPool, &aSet ); + ApplySelectionCache( &aCache, rMark ); + } +} + + +// pTable = Clipboard +void ScTable::CopyToClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ScTable* pTable, BOOL bKeepScenarioFlags, BOOL bCloneNoteCaptions) +{ + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + { + // Inhalte kopieren + SCCOL i; + + for ( i = nCol1; i <= nCol2; i++) + aCol[i].CopyToClip(nRow1, nRow2, pTable->aCol[i], bKeepScenarioFlags, bCloneNoteCaptions); + + // copy widths/heights, and only "hidden", "filtered" and "manual" flags + // also for all preceding columns/rows, to have valid positions for drawing objects + + if (pColFlags && pTable->pColFlags && pColWidth && pTable->pColWidth) + for (i=0; i<=nCol2; i++) + { + pTable->pColFlags[i] = pColFlags[i] & CR_HIDDEN; + pTable->pColWidth[i] = pColWidth[i]; + } + + if (pRowFlags && pTable->pRowFlags && pRowHeight && pTable->pRowHeight) + { + pTable->pRowFlags->CopyFromAnded( *pRowFlags, 0, nRow2, + (CR_HIDDEN | CR_FILTERED | CR_MANUALSIZE)); + pTable->pRowHeight->CopyFrom( *pRowHeight, 0, nRow2); + } + + + // ggf. Formeln durch Werte ersetzen + + if ( IsProtected() ) + for (i = nCol1; i <= nCol2; i++) + pTable->aCol[i].RemoveProtected(nRow1, nRow2); + } +} + +void ScTable::CopyToClip(const ScRangeList& rRanges, ScTable* pTable, + bool bKeepScenarioFlags, bool bCloneNoteCaptions) +{ + ScRangeList aRanges(rRanges); + for (ScRangePtr p = aRanges.First(); p; p = aRanges.Next()) + { + CopyToClip(p->aStart.Col(), p->aStart.Row(), p->aEnd.Col(), p->aEnd.Row(), + pTable, bKeepScenarioFlags, bCloneNoteCaptions); + } +} + +void ScTable::CopyFromClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + SCsCOL nDx, SCsROW nDy, USHORT nInsFlag, + BOOL bAsLink, BOOL bSkipAttrForEmpty, ScTable* pTable) +{ + SCCOL i; + + if (nCol2 > MAXCOL) nCol2 = MAXCOL; + if (nRow2 > MAXROW) nRow2 = MAXROW; + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + { + nRecalcLvl++; + for ( i = nCol1; i <= nCol2; i++) + aCol[i].CopyFromClip(nRow1, nRow2, nDy, nInsFlag, bAsLink, bSkipAttrForEmpty, pTable->aCol[i - nDx]); + + if ((nInsFlag & IDF_ATTRIB) != 0) + { + if (nRow1==0 && nRow2==MAXROW && pColWidth && pTable->pColWidth) + for (i=nCol1; i<=nCol2; i++) + pColWidth[i] = pTable->pColWidth[i-nDx]; + + if (nCol1==0 && nCol2==MAXCOL && pRowHeight && pTable->pRowHeight && + pRowFlags && pTable->pRowFlags) + { + pRowHeight->CopyFrom( *pTable->pRowHeight, nRow1, nRow2, -nDy); + // Must copy CR_MANUALSIZE bit too, otherwise pRowHeight doesn't make sense + for (SCROW j=nRow1; j<=nRow2; j++) + { + if ( pTable->pRowFlags->GetValue(j-nDy) & CR_MANUALSIZE ) + pRowFlags->OrValue( j, CR_MANUALSIZE); + else + pRowFlags->AndValue( j, sal::static_int_cast<BYTE>(~CR_MANUALSIZE)); + } + } + + // + // Zellschutz auf geschuetzter Tabelle nicht setzen + // + + if ( IsProtected() && (nInsFlag & IDF_ATTRIB) ) + { + ScPatternAttr aPattern(pDocument->GetPool()); + aPattern.GetItemSet().Put( ScProtectionAttr( FALSE ) ); + ApplyPatternArea( nCol1, nRow1, nCol2, nRow2, aPattern ); + } + } + if( !--nRecalcLvl ) + SetDrawPageSize(); + } +} + + +void ScTable::MixData( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nFunction, BOOL bSkipEmpty, ScTable* pSrcTab ) +{ + for (SCCOL i=nCol1; i<=nCol2; i++) + aCol[i].MixData( nRow1, nRow2, nFunction, bSkipEmpty, pSrcTab->aCol[i] ); +} + + +// Markierung von diesem Dokument +void ScTable::MixMarked( const ScMarkData& rMark, USHORT nFunction, + BOOL bSkipEmpty, ScTable* pSrcTab ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].MixMarked( rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i] ); +} + + +void ScTable::TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ScTable* pTransClip, USHORT nFlags, BOOL bAsLink ) +{ + BOOL bWasCut = pDocument->IsCutMode(); + + ScDocument* pDestDoc = pTransClip->pDocument; + + for (SCCOL nCol=nCol1; nCol<=nCol2; nCol++) + { + SCROW nRow; + ScBaseCell* pCell; + + if ( bAsLink && nFlags == IDF_ALL ) + { + // #68989# with IDF_ALL, also create links (formulas) for empty cells + + for ( nRow=nRow1; nRow<=nRow2; nRow++ ) + { + // create simple formula, as in ScColumn::CreateRefCell + + ScAddress aDestPos( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); + ScSingleRefData aRef; + aRef.nCol = nCol; + aRef.nRow = nRow; + aRef.nTab = nTab; + aRef.InitFlags(); // -> all absolute + aRef.SetFlag3D(TRUE); + aRef.CalcRelFromAbs( aDestPos ); + ScTokenArray aArr; + aArr.AddSingleReference( aRef ); + + ScBaseCell* pNew = new ScFormulaCell( pDestDoc, aDestPos, &aArr ); + pTransClip->PutCell( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pNew ); + } + } + else + { + ScColumnIterator aIter( &aCol[nCol], nRow1, nRow2 ); + while (aIter.Next( nRow, pCell )) + { + ScAddress aDestPos( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pTransClip->nTab ); + ScBaseCell* pNew; + if ( bAsLink ) // Referenz erzeugen ? + { + pNew = aCol[nCol].CreateRefCell( pDestDoc, aDestPos, aIter.GetIndex(), nFlags ); + } + else // kopieren + { + ScAddress aOwnPos( nCol, nRow, nTab ); + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + pNew = pCell->CloneWithNote( aOwnPos, *pDestDoc, aDestPos, SC_CLONECELL_STARTLISTENING ); + + // Referenzen drehen + // bei Cut werden Referenzen spaeter per UpdateTranspose angepasst + + if (!bWasCut) + ((ScFormulaCell*)pNew)->TransposeReference(); + } + else + { + pNew = pCell->CloneWithNote( aOwnPos, *pDestDoc, aDestPos ); + } + } + pTransClip->PutCell( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), pNew ); + } + } + + // Attribute + + SCROW nAttrRow1; + SCROW nAttrRow2; + const ScPatternAttr* pPattern; + ScAttrIterator* pAttrIter = aCol[nCol].CreateAttrIterator( nRow1, nRow2 ); + while ( (pPattern = pAttrIter->Next( nAttrRow1, nAttrRow2 )) != 0 ) + { + if ( !IsDefaultItem( pPattern ) ) + { + const SfxItemSet& rSet = pPattern->GetItemSet(); + if ( rSet.GetItemState( ATTR_MERGE, FALSE ) == SFX_ITEM_DEFAULT && + rSet.GetItemState( ATTR_MERGE_FLAG, FALSE ) == SFX_ITEM_DEFAULT && + rSet.GetItemState( ATTR_BORDER, FALSE ) == SFX_ITEM_DEFAULT ) + { + // no borders or merge items involved - use pattern as-is + for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++) + pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1), static_cast<SCROW>(nCol-nCol1), *pPattern, TRUE ); + } + else + { + // transpose borders and merge values, remove merge flags (refreshed after pasting) + ScPatternAttr aNewPattern( *pPattern ); + SfxItemSet& rNewSet = aNewPattern.GetItemSet(); + + const SvxBoxItem& rOldBox = (const SvxBoxItem&)rSet.Get(ATTR_BORDER); + if ( rOldBox.GetTop() || rOldBox.GetBottom() || rOldBox.GetLeft() || rOldBox.GetRight() ) + { + SvxBoxItem aNew( ATTR_BORDER ); + aNew.SetLine( rOldBox.GetLine( BOX_LINE_TOP ), BOX_LINE_LEFT ); + aNew.SetLine( rOldBox.GetLine( BOX_LINE_LEFT ), BOX_LINE_TOP ); + aNew.SetLine( rOldBox.GetLine( BOX_LINE_BOTTOM ), BOX_LINE_RIGHT ); + aNew.SetLine( rOldBox.GetLine( BOX_LINE_RIGHT ), BOX_LINE_BOTTOM ); + aNew.SetDistance( rOldBox.GetDistance( BOX_LINE_TOP ), BOX_LINE_LEFT ); + aNew.SetDistance( rOldBox.GetDistance( BOX_LINE_LEFT ), BOX_LINE_TOP ); + aNew.SetDistance( rOldBox.GetDistance( BOX_LINE_BOTTOM ), BOX_LINE_RIGHT ); + aNew.SetDistance( rOldBox.GetDistance( BOX_LINE_RIGHT ), BOX_LINE_BOTTOM ); + rNewSet.Put( aNew ); + } + + const ScMergeAttr& rOldMerge = (const ScMergeAttr&)rSet.Get(ATTR_MERGE); + if (rOldMerge.IsMerged()) + rNewSet.Put( ScMergeAttr( Min( + static_cast<SCsCOL>(rOldMerge.GetRowMerge()), + static_cast<SCsCOL>(MAXCOL+1 - (nAttrRow2-nRow1))), + Min( + static_cast<SCsROW>(rOldMerge.GetColMerge()), + static_cast<SCsROW>(MAXROW+1 - (nCol-nCol1))))); + const ScMergeFlagAttr& rOldFlag = (const ScMergeFlagAttr&)rSet.Get(ATTR_MERGE_FLAG); + if (rOldFlag.IsOverlapped()) + { + INT16 nNewFlags = rOldFlag.GetValue() & ~( SC_MF_HOR | SC_MF_VER ); + if ( nNewFlags ) + rNewSet.Put( ScMergeFlagAttr( nNewFlags ) ); + else + rNewSet.ClearItem( ATTR_MERGE_FLAG ); + } + + for (nRow = nAttrRow1; nRow<=nAttrRow2; nRow++) + pTransClip->SetPattern( static_cast<SCCOL>(nRow-nRow1), + static_cast<SCROW>(nCol-nCol1), aNewPattern, TRUE); + } + } + } + + delete pAttrIter; + } +} + + +void ScTable::StartAllListeners() +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].StartAllListeners(); +} + + +void ScTable::StartNeededListeners() +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].StartNeededListeners(); +} + + +void ScTable::BroadcastInArea( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2 ) +{ + if (nCol2 > MAXCOL) nCol2 = MAXCOL; + if (nRow2 > MAXROW) nRow2 = MAXROW; + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + for (SCCOL i = nCol1; i <= nCol2; i++) + aCol[i].BroadcastInArea( nRow1, nRow2 ); +} + + +void ScTable::StartListeningInArea( SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2 ) +{ + if (nCol2 > MAXCOL) nCol2 = MAXCOL; + if (nRow2 > MAXROW) nRow2 = MAXROW; + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + for (SCCOL i = nCol1; i <= nCol2; i++) + aCol[i].StartListeningInArea( nRow1, nRow2 ); +} + + +void ScTable::CopyToTable(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nFlags, BOOL bMarked, ScTable* pDestTab, + const ScMarkData* pMarkData, + BOOL bAsLink, BOOL bColRowFlags) +{ + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + { + if (nFlags) + for (SCCOL i = nCol1; i <= nCol2; i++) + aCol[i].CopyToColumn(nRow1, nRow2, nFlags, bMarked, + pDestTab->aCol[i], pMarkData, bAsLink); + + if (bColRowFlags) // Spaltenbreiten/Zeilenhoehen/Flags + { + // Charts muessen beim Ein-/Ausblenden angepasst werden + ScChartListenerCollection* pCharts = pDestTab->pDocument->GetChartListenerCollection(); + + BOOL bWidth = (nRow1==0 && nRow2==MAXROW && pColWidth && pDestTab->pColWidth); + BOOL bHeight = (nCol1==0 && nCol2==MAXCOL && pRowHeight && pDestTab->pRowHeight); + + if (bWidth||bHeight) + { + pDestTab->IncRecalcLevel(); + + if (bWidth) + for (SCCOL i=nCol1; i<=nCol2; i++) + { + BOOL bChange = pCharts && + ( pDestTab->pColFlags[i] & CR_HIDDEN ) != ( pColFlags[i] & CR_HIDDEN ); + pDestTab->pColWidth[i] = pColWidth[i]; + pDestTab->pColFlags[i] = pColFlags[i]; + //! Aenderungen zusammenfassen? + if (bChange) + pCharts->SetRangeDirty(ScRange( i, 0, nTab, i, MAXROW, nTab )); + } + + if (bHeight) + { + pDestTab->pRowHeight->CopyFrom( *pRowHeight, nRow1, nRow2); + for (SCROW i=nRow1; i<=nRow2; i++) + { + // TODO: might need some performance improvement, block + // operations instead of single GetValue()/SetValue() calls. + BYTE nThisRowFlags = pRowFlags->GetValue(i); + BOOL bChange = pCharts && + ( pDestTab->pRowFlags->GetValue(i) & CR_HIDDEN ) != ( nThisRowFlags & CR_HIDDEN ); + pDestTab->pRowFlags->SetValue( i, nThisRowFlags ); + //! Aenderungen zusammenfassen? + if (bChange) + pCharts->SetRangeDirty(ScRange( 0, i, nTab, MAXCOL, i, nTab )); + } + } + pDestTab->DecRecalcLevel(); + } + pDestTab->SetOutlineTable( pOutlineTable ); // auch nur wenn bColRowFlags + } + } +} + + +void ScTable::UndoToTable(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + USHORT nFlags, BOOL bMarked, ScTable* pDestTab, + const ScMarkData* pMarkData) +{ + if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)) + { + BOOL bWidth = (nRow1==0 && nRow2==MAXROW && pColWidth && pDestTab->pColWidth); + BOOL bHeight = (nCol1==0 && nCol2==MAXCOL && pRowHeight && pDestTab->pRowHeight); + + if (bWidth||bHeight) + nRecalcLvl++; + + for ( SCCOL i = 0; i <= MAXCOL; i++) + { + if ( i >= nCol1 && i <= nCol2 ) + aCol[i].UndoToColumn(nRow1, nRow2, nFlags, bMarked, pDestTab->aCol[i], + pMarkData); + else + aCol[i].CopyToColumn(0, MAXROW, IDF_FORMULA, FALSE, pDestTab->aCol[i]); + } + + if (bWidth||bHeight) + { + if (bWidth) + for (SCCOL i=nCol1; i<=nCol2; i++) + pDestTab->pColWidth[i] = pColWidth[i]; + if (bHeight) + pDestTab->pRowHeight->CopyFrom( *pRowHeight, nRow1, nRow2); + if( !--nRecalcLvl ) + SetDrawPageSize(); + } + } +} + + +void ScTable::CopyUpdated( const ScTable* pPosTab, ScTable* pDestTab ) const +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].CopyUpdated( pPosTab->aCol[i], pDestTab->aCol[i] ); +} + +void ScTable::CopyScenarioTo( ScTable* pDestTab ) const +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].CopyScenarioTo( pDestTab->aCol[i] ); +} + +void ScTable::CopyScenarioFrom( const ScTable* pSrcTab ) +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].CopyScenarioFrom( pSrcTab->aCol[i] ); +} + +void ScTable::MarkScenarioIn( ScMarkData& rDestMark, USHORT nNeededBits ) const +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + + if ( ( nScenarioFlags & nNeededBits ) != nNeededBits ) // alle Bits gesetzt? + return; + + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].MarkScenarioIn( rDestMark ); +} + +BOOL ScTable::HasScenarioRange( const ScRange& rRange ) const +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + +// ScMarkData aMark; +// MarkScenarioIn( aMark, 0 ); //! Bits als Parameter von HasScenarioRange? +// return aMark.IsAllMarked( rRange ); + + ScRange aTabRange = rRange; + aTabRange.aStart.SetTab( nTab ); + aTabRange.aEnd.SetTab( nTab ); + + const ScRangeList* pList = GetScenarioRanges(); +// return ( pList && pList->Find( aTabRange ) ); + + if (pList) + { + ULONG nCount = pList->Count(); + for ( ULONG j = 0; j < nCount; j++ ) + { + ScRange* pR = pList->GetObject( j ); + if ( pR->Intersects( aTabRange ) ) + return TRUE; + } + } + + return FALSE; +} + +void ScTable::InvalidateScenarioRanges() +{ + delete pScenarioRanges; + pScenarioRanges = NULL; +} + +const ScRangeList* ScTable::GetScenarioRanges() const +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + + if (!pScenarioRanges) + { + ((ScTable*)this)->pScenarioRanges = new ScRangeList; + ScMarkData aMark; + MarkScenarioIn( aMark, 0 ); // immer + aMark.FillRangeListWithMarks( pScenarioRanges, FALSE ); + } + return pScenarioRanges; +} + +BOOL ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const +{ + DBG_ASSERT( bScenario, "bScenario == FALSE" ); + + if (!pDestTab->IsProtected()) + return TRUE; + + BOOL bOk = TRUE; + for (SCCOL i=0; i<=MAXCOL && bOk; i++) + bOk = aCol[i].TestCopyScenarioTo( pDestTab->aCol[i] ); + return bOk; +} + +void ScTable::PutCell( SCCOL nCol, SCROW nRow, ScBaseCell* pCell ) +{ + if (ValidColRow(nCol,nRow)) + { + if (pCell) + aCol[nCol].Insert( nRow, pCell ); + else + aCol[nCol].Delete( nRow ); + } +} + + +void ScTable::PutCell( SCCOL nCol, SCROW nRow, ULONG nFormatIndex, ScBaseCell* pCell ) +{ + if (ValidColRow(nCol,nRow)) + { + if (pCell) + aCol[nCol].Insert( nRow, nFormatIndex, pCell ); + else + aCol[nCol].Delete( nRow ); + } +} + + +void ScTable::PutCell( const ScAddress& rPos, ScBaseCell* pCell ) +{ + if (pCell) + aCol[rPos.Col()].Insert( rPos.Row(), pCell ); + else + aCol[rPos.Col()].Delete( rPos.Row() ); +} + + +//UNUSED2009-05 void ScTable::PutCell( const ScAddress& rPos, ULONG nFormatIndex, ScBaseCell* pCell ) +//UNUSED2009-05 { +//UNUSED2009-05 if (pCell) +//UNUSED2009-05 aCol[rPos.Col()].Insert( rPos.Row(), nFormatIndex, pCell ); +//UNUSED2009-05 else +//UNUSED2009-05 aCol[rPos.Col()].Delete( rPos.Row() ); +//UNUSED2009-05 } + + +BOOL ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const String& rString ) +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].SetString( nRow, nTabP, rString ); + else + return FALSE; +} + + +void ScTable::SetValue( SCCOL nCol, SCROW nRow, const double& rVal ) +{ + if (ValidColRow(nCol, nRow)) + aCol[nCol].SetValue( nRow, rVal ); +} + + +void ScTable::GetString( SCCOL nCol, SCROW nRow, String& rString ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].GetString( nRow, rString ); + else + rString.Erase(); +} + + +void ScTable::GetInputString( SCCOL nCol, SCROW nRow, String& rString ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].GetInputString( nRow, rString ); + else + rString.Erase(); +} + + +double ScTable::GetValue( SCCOL nCol, SCROW nRow ) +{ + if (ValidColRow( nCol, nRow )) + return aCol[nCol].GetValue( nRow ); + return 0.0; +} + + +void ScTable::GetFormula( SCCOL nCol, SCROW nRow, String& rFormula, + BOOL bAsciiExport ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].GetFormula( nRow, rFormula, bAsciiExport ); + else + rFormula.Erase(); +} + + +ScPostIt* ScTable::GetNote( SCCOL nCol, SCROW nRow ) +{ + return ValidColRow( nCol, nRow ) ? aCol[ nCol ].GetNote( nRow ) : 0; +} + + +void ScTable::TakeNote( SCCOL nCol, SCROW nRow, ScPostIt*& rpNote ) +{ + if( ValidColRow( nCol, nRow ) ) + { + aCol[ nCol ].TakeNote( nRow, rpNote ); + if( rpNote && rpNote->GetNoteData().mxInitData.get() ) + { + if( !mxUninitNotes.get() ) + mxUninitNotes.reset( new ScAddress2DVec ); + mxUninitNotes->push_back( ScAddress2D( nCol, nRow ) ); + } + } + else + DELETEZ( rpNote ); +} + + +ScPostIt* ScTable::ReleaseNote( SCCOL nCol, SCROW nRow ) +{ + return ValidColRow( nCol, nRow ) ? aCol[ nCol ].ReleaseNote( nRow ) : 0; +} + + +void ScTable::DeleteNote( SCCOL nCol, SCROW nRow ) +{ + if( ValidColRow( nCol, nRow ) ) + aCol[ nCol ].DeleteNote( nRow ); +} + + +void ScTable::InitializeNoteCaptions( bool bForced ) +{ + if( mxUninitNotes.get() && (bForced || pDocument->IsUndoEnabled()) ) + { + for( ScAddress2DVec::iterator aIt = mxUninitNotes->begin(), aEnd = mxUninitNotes->end(); aIt != aEnd; ++aIt ) + if( ScPostIt* pNote = GetNote( aIt->first, aIt->second ) ) + pNote->GetOrCreateCaption( ScAddress( aIt->first, aIt->second, nTab ) ); + mxUninitNotes.reset(); + } +} + +CellType ScTable::GetCellType( SCCOL nCol, SCROW nRow ) const +{ + if (ValidColRow( nCol, nRow )) + return aCol[nCol].GetCellType( nRow ); + return CELLTYPE_NONE; +} + + +ScBaseCell* ScTable::GetCell( SCCOL nCol, SCROW nRow ) const +{ + if (ValidColRow( nCol, nRow )) + return aCol[nCol].GetCell( nRow ); + + DBG_ERROR("GetCell ausserhalb"); + return NULL; +} + + +void ScTable::GetLastDataPos(SCCOL& rCol, SCROW& rRow) const +{ + rCol = MAXCOL; + rRow = 0; + while (aCol[rCol].IsEmptyData() && (rCol > 0)) + rCol--; + SCCOL nCol = rCol; + while ((SCsCOL)nCol >= 0) + { + rRow = Max(rRow, aCol[nCol].GetLastDataPos()); + nCol--; + } +} + + +BOOL ScTable::HasData( SCCOL nCol, SCROW nRow ) +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].HasDataAt( nRow ); + else + return FALSE; +} + + +BOOL ScTable::HasStringData( SCCOL nCol, SCROW nRow ) +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].HasStringData( nRow ); + else + return FALSE; +} + + +BOOL ScTable::HasValueData( SCCOL nCol, SCROW nRow ) +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].HasValueData( nRow ); + else + return FALSE; +} + + +BOOL ScTable::HasStringCells( SCCOL nStartCol, SCROW nStartRow, + SCCOL nEndCol, SCROW nEndRow ) const +{ + if ( ValidCol(nEndCol) ) + for ( SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++ ) + if (aCol[nCol].HasStringCells(nStartRow, nEndRow)) + return TRUE; + + return FALSE; +} + + +//UNUSED2008-05 USHORT ScTable::GetErrCode( SCCOL nCol, SCROW nRow ) const +//UNUSED2008-05 { +//UNUSED2008-05 if (ValidColRow( nCol, nRow )) +//UNUSED2008-05 return aCol[nCol].GetErrCode( nRow ); +//UNUSED2008-05 return 0; +//UNUSED2008-05 } + + +void ScTable::SetDirtyVar() +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].SetDirtyVar(); +} + + +void ScTable::SetDirty() +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].SetDirty(); + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScTable::SetDirty( const ScRange& rRange ) +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + SCCOL nCol2 = rRange.aEnd.Col(); + for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++) + aCol[i].SetDirty( rRange ); + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScTable::SetTableOpDirty( const ScRange& rRange ) +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // no multiple recalculation + SCCOL nCol2 = rRange.aEnd.Col(); + for (SCCOL i=rRange.aStart.Col(); i<=nCol2; i++) + aCol[i].SetTableOpDirty( rRange ); + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScTable::SetDirtyAfterLoad() +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].SetDirtyAfterLoad(); + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScTable::SetRelNameDirty() +{ + BOOL bOldAutoCalc = pDocument->GetAutoCalc(); + pDocument->SetAutoCalc( FALSE ); // Mehrfachberechnungen vermeiden + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].SetRelNameDirty(); + pDocument->SetAutoCalc( bOldAutoCalc ); +} + + +void ScTable::CalcAll() +{ + for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CalcAll(); +} + + +void ScTable::CompileAll() +{ + for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].CompileAll(); +} + + +void ScTable::CompileXML( ScProgress& rProgress ) +{ + for (SCCOL i=0; i <= MAXCOL; i++) + { + aCol[i].CompileXML( rProgress ); + } +} + +void ScTable::CalcAfterLoad() +{ + for (SCCOL i=0; i <= MAXCOL; i++) aCol[i].CalcAfterLoad(); +} + + +bool ScTable::MarkUsedExternalReferences() +{ + bool bAllMarked = false; + for (SCCOL i=0; i <= MAXCOL && !bAllMarked; ++i) + { + bAllMarked = aCol[i].MarkUsedExternalReferences(); + } + return bAllMarked; +} + + +void ScTable::ResetChanged( const ScRange& rRange ) +{ + SCCOL nStartCol = rRange.aStart.Col(); + SCROW nStartRow = rRange.aStart.Row(); + SCCOL nEndCol = rRange.aEnd.Col(); + SCROW nEndRow = rRange.aEnd.Row(); + + for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) + aCol[nCol].ResetChanged(nStartRow, nEndRow); +} + +// Attribute + +const SfxPoolItem* ScTable::GetAttr( SCCOL nCol, SCROW nRow, USHORT nWhich ) const +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].GetAttr( nRow, nWhich ); + else + return NULL; +} + + +ULONG ScTable::GetNumberFormat( SCCOL nCol, SCROW nRow ) const +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].GetNumberFormat( nRow ); + else + return 0; +} + + +const ScPatternAttr* ScTable::GetPattern( SCCOL nCol, SCROW nRow ) const +{ + if (ValidColRow(nCol,nRow)) + return aCol[nCol].GetPattern( nRow ); + else + { + DBG_ERROR("wrong column or row"); + return pDocument->GetDefPattern(); // for safety + } +} + + +const ScPatternAttr* ScTable::GetMostUsedPattern( SCCOL nCol, SCROW nStartRow, SCROW nEndRow ) const +{ + if ( ValidColRow( nCol, nStartRow ) && ValidRow( nEndRow ) && (nStartRow <= nEndRow) ) + return aCol[nCol].GetMostUsedPattern( nStartRow, nEndRow ); + else + return NULL; +} + + +BOOL ScTable::HasAttrib( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, USHORT nMask ) const +{ + BOOL bFound=FALSE; + for (SCCOL i=nCol1; i<=nCol2 && !bFound; i++) + bFound |= aCol[i].HasAttrib( nRow1, nRow2, nMask ); + return bFound; +} + + +//UNUSED2009-05 BOOL ScTable::HasLines( const ScRange& rRange, Rectangle& rSizes ) const +//UNUSED2009-05 { +//UNUSED2009-05 SCCOL nCol1 = rRange.aStart.Col(); +//UNUSED2009-05 SCROW nRow1 = rRange.aStart.Row(); +//UNUSED2009-05 SCCOL nCol2 = rRange.aEnd.Col(); +//UNUSED2009-05 SCROW nRow2 = rRange.aEnd.Row(); +//UNUSED2009-05 PutInOrder( nCol1, nCol2 ); +//UNUSED2009-05 PutInOrder( nRow1, nRow2 ); +//UNUSED2009-05 +//UNUSED2009-05 BOOL bFound = FALSE; +//UNUSED2009-05 for (SCCOL i=nCol1; i<=nCol2; i++) +//UNUSED2009-05 if (aCol[i].HasLines( nRow1, nRow2, rSizes, (i==nCol1), (i==nCol2) )) +//UNUSED2009-05 bFound = TRUE; +//UNUSED2009-05 +//UNUSED2009-05 return bFound; +//UNUSED2009-05 } + + +BOOL ScTable::HasAttribSelection( const ScMarkData& rMark, USHORT nMask ) const +{ + BOOL bFound=FALSE; + for (SCCOL i=0; i<=MAXCOL && !bFound; i++) + bFound |= aCol[i].HasAttribSelection( rMark, nMask ); + return bFound; +} + + +BOOL ScTable::ExtendMerge( SCCOL nStartCol, SCROW nStartRow, + SCCOL& rEndCol, SCROW& rEndRow, + BOOL bRefresh, BOOL bAttrs ) +{ + if (!(ValidCol(nStartCol) && ValidCol(rEndCol))) + { + DBG_ERRORFILE("ScTable::ExtendMerge: invalid column number"); + return FALSE; + } + BOOL bFound=FALSE; + SCCOL nOldEndX = rEndCol; + SCROW nOldEndY = rEndRow; + for (SCCOL i=nStartCol; i<=nOldEndX; i++) + bFound |= aCol[i].ExtendMerge( i, nStartRow, nOldEndY, rEndCol, rEndRow, bRefresh, bAttrs ); + return bFound; +} + + +BOOL ScTable::IsBlockEmpty( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, bool bIgnoreNotes ) const +{ + if (!(ValidCol(nCol1) && ValidCol(nCol2))) + { + DBG_ERRORFILE("ScTable::IsBlockEmpty: invalid column number"); + return FALSE; + } + BOOL bEmpty = TRUE; + for (SCCOL i=nCol1; i<=nCol2 && bEmpty; i++) + bEmpty = aCol[i].IsEmptyBlock( nRow1, nRow2, bIgnoreNotes ); + return bEmpty; +} + +SCSIZE ScTable::FillMaxRot( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2, + SCCOL nCol, SCROW nAttrRow1, SCROW nAttrRow2, SCSIZE nArrY, + const ScPatternAttr* pPattern, const SfxItemSet* pCondSet ) const +{ + // Rueckgabe = neues nArrY + + BYTE nRotDir = pPattern->GetRotateDir( pCondSet ); + if ( nRotDir != SC_ROTDIR_NONE ) + { + BOOL bHit = TRUE; + if ( nCol+1 < nX1 ) // column to the left + bHit = ( nRotDir != SC_ROTDIR_LEFT ); + else if ( nCol > nX2+1 ) // column to the right + bHit = ( nRotDir != SC_ROTDIR_RIGHT ); // SC_ROTDIR_STANDARD may now also be extended to the left + + if ( bHit ) + { + double nFactor = 0.0; + if ( nCol > nX2+1 ) + { + long nRotVal = ((const SfxInt32Item&) pPattern-> + GetItem( ATTR_ROTATE_VALUE, pCondSet )).GetValue(); + double nRealOrient = nRotVal * F_PI18000; // 1/100 Grad + double nCos = cos( nRealOrient ); + double nSin = sin( nRealOrient ); + //! begrenzen !!! + //! zusaetzlich Faktor fuer unterschiedliche PPT X/Y !!! + + // bei SC_ROTDIR_LEFT kommt immer ein negativer Wert heraus, + // wenn der Modus beruecksichtigt wird + nFactor = -fabs( nCos / nSin ); + } + + for ( SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++ ) + { + if ( !(pRowFlags->GetValue(nRow) & CR_HIDDEN) ) + { + BOOL bHitOne = TRUE; + if ( nCol > nX2+1 ) + { + // reicht die gedrehte Zelle bis in den sichtbaren Bereich? + + SCCOL nTouchedCol = nCol; + long nWidth = (long) ( pRowHeight->GetValue(nRow) * nFactor ); + DBG_ASSERT(nWidth <= 0, "Richtung falsch"); + while ( nWidth < 0 && nTouchedCol > 0 ) + { + --nTouchedCol; + nWidth += GetColWidth( nTouchedCol ); + } + if ( nTouchedCol > nX2 ) + bHitOne = FALSE; + } + + if (bHitOne) + { + while ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo < nRow ) + ++nArrY; + if ( nArrY<nArrCount && pRowInfo[nArrY].nRowNo == nRow ) + pRowInfo[nArrY].nRotMaxCol = nCol; + } + } + } + } + } + + return nArrY; +} + +void ScTable::FindMaxRotCol( RowInfo* pRowInfo, SCSIZE nArrCount, SCCOL nX1, SCCOL nX2 ) const +{ + if ( !pColWidth || !pRowHeight || !pColFlags || !pRowFlags ) + { + DBG_ERROR( "Spalten-/Zeileninfo fehlt" ); + return; + } + + // nRotMaxCol ist auf SC_ROTMAX_NONE initialisiert, nRowNo ist schon gesetzt + + SCROW nY1 = pRowInfo[0].nRowNo; + SCROW nY2 = pRowInfo[nArrCount-1].nRowNo; + + for (SCCOL nCol=0; nCol<=MAXCOL; nCol++) + { + if ( !(pColFlags[nCol] & CR_HIDDEN) ) + { + SCSIZE nArrY = 0; + ScDocAttrIterator aIter( pDocument, nTab, nCol, nY1, nCol, nY2 ); + SCCOL nAttrCol; + SCROW nAttrRow1, nAttrRow2; + const ScPatternAttr* pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 ); + while ( pPattern ) + { + const SfxPoolItem* pCondItem; + if ( pPattern->GetItemSet().GetItemState( ATTR_CONDITIONAL, TRUE, &pCondItem ) + == SFX_ITEM_SET ) + { + // alle Formate durchgehen, damit die Zellen nicht einzeln + // angeschaut werden muessen + + ULONG nIndex = ((const SfxUInt32Item*)pCondItem)->GetValue(); + ScConditionalFormatList* pList = pDocument->GetCondFormList(); + ScStyleSheetPool* pStylePool = pDocument->GetStyleSheetPool(); + if (pList && pStylePool && nIndex) + { + const ScConditionalFormat* pFormat = pList->GetFormat(nIndex); + if ( pFormat ) + { + USHORT nEntryCount = pFormat->Count(); + for (USHORT nEntry=0; nEntry<nEntryCount; nEntry++) + { + String aStyleName = pFormat->GetEntry(nEntry)->GetStyle(); + if (aStyleName.Len()) + { + SfxStyleSheetBase* pStyleSheet = + pStylePool->Find( aStyleName, SFX_STYLE_FAMILY_PARA ); + if ( pStyleSheet ) + { + FillMaxRot( pRowInfo, nArrCount, nX1, nX2, + nCol, nAttrRow1, nAttrRow2, + nArrY, pPattern, &pStyleSheet->GetItemSet() ); + // nArrY nicht veraendern + } + } + } + } + } + } + + nArrY = FillMaxRot( pRowInfo, nArrCount, nX1, nX2, + nCol, nAttrRow1, nAttrRow2, + nArrY, pPattern, NULL ); + + pPattern = aIter.GetNext( nAttrCol, nAttrRow1, nAttrRow2 ); + } + } + } +} + +BOOL ScTable::HasBlockMatrixFragment( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const +{ + // nix:0, mitte:1, unten:2, links:4, oben:8, rechts:16, offen:32 + USHORT nEdges; + + if ( nCol1 == nCol2 ) + { // linke und rechte Spalte + const USHORT n = 4 | 16; + nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, n ); + // nicht (4 und 16) oder 1 oder 32 + if ( nEdges && (((nEdges & n) != n) || (nEdges & 33)) ) + return TRUE; // linke oder rechte Kante fehlt oder offen + } + else + { // linke Spalte + nEdges = aCol[nCol1].GetBlockMatrixEdges( nRow1, nRow2, 4 ); + // nicht 4 oder 1 oder 32 + if ( nEdges && (((nEdges & 4) != 4) || (nEdges & 33)) ) + return TRUE; // linke Kante fehlt oder offen + // rechte Spalte + nEdges = aCol[nCol2].GetBlockMatrixEdges( nRow1, nRow2, 16 ); + // nicht 16 oder 1 oder 32 + if ( nEdges && (((nEdges & 16) != 16) || (nEdges & 33)) ) + return TRUE; // rechte Kante fehlt oder offen + } + + if ( nRow1 == nRow2 ) + { // obere und untere Zeile + BOOL bOpen = FALSE; + const USHORT n = 2 | 8; + for ( SCCOL i=nCol1; i<=nCol2; i++) + { + nEdges = aCol[i].GetBlockMatrixEdges( nRow1, nRow1, n ); + if ( nEdges ) + { + if ( (nEdges & n) != n ) + return TRUE; // obere oder untere Kante fehlt + if ( nEdges & 4 ) + bOpen = TRUE; // linke Kante oeffnet, weitersehen + else if ( !bOpen ) + return TRUE; // es gibt was, was nicht geoeffnet wurde + if ( nEdges & 16 ) + bOpen = FALSE; // rechte Kante schliesst + } + } + if ( bOpen ) + return TRUE; // es geht noch weiter + } + else + { + USHORT j, n; + SCROW nR; + // erst obere Zeile, dann untere Zeile + for ( j=0, nR=nRow1, n=8; j<2; j++, nR=nRow2, n=2 ) + { + BOOL bOpen = FALSE; + for ( SCCOL i=nCol1; i<=nCol2; i++) + { + nEdges = aCol[i].GetBlockMatrixEdges( nR, nR, n ); + if ( nEdges ) + { + // in oberere Zeile keine obere Kante bzw. + // in unterer Zeile keine untere Kante + if ( (nEdges & n) != n ) + return TRUE; + if ( nEdges & 4 ) + bOpen = TRUE; // linke Kante oeffnet, weitersehen + else if ( !bOpen ) + return TRUE; // es gibt was, was nicht geoeffnet wurde + if ( nEdges & 16 ) + bOpen = FALSE; // rechte Kante schliesst + } + } + if ( bOpen ) + return TRUE; // es geht noch weiter + } + } + return FALSE; +} + + +BOOL ScTable::HasSelectionMatrixFragment( const ScMarkData& rMark ) const +{ + BOOL bFound=FALSE; + for (SCCOL i=0; i<=MAXCOL && !bFound; i++) + bFound |= aCol[i].HasSelectionMatrixFragment(rMark); + return bFound; +} + + +BOOL ScTable::IsBlockEditable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, + SCROW nRow2, BOOL* pOnlyNotBecauseOfMatrix /* = NULL */ ) const +{ + if ( !ValidColRow( nCol2, nRow2 ) ) + { + DBG_ERRORFILE("IsBlockEditable: invalid column or row"); + if (pOnlyNotBecauseOfMatrix) + *pOnlyNotBecauseOfMatrix = FALSE; + return FALSE; + } + + BOOL bIsEditable = TRUE; + if ( nLockCount ) + bIsEditable = FALSE; + else if ( IsProtected() && !pDocument->IsScenario(nTab) ) + { + if((bIsEditable = !HasAttrib( nCol1, nRow1, nCol2, nRow2, HASATTR_PROTECTED )) != FALSE) + { + // If Sheet is protected and cells are not protected then + // check the active scenario protect flag if this range is + // on the active scenario range. Note the 'copy back' must also + // be set to apply protection. + USHORT nScenTab = nTab+1; + while(pDocument->IsScenario(nScenTab)) + { + ScRange aEditRange(nCol1, nRow1, nScenTab, nCol2, nRow2, nScenTab); + if(pDocument->IsActiveScenario(nScenTab) && pDocument->HasScenarioRange(nScenTab, aEditRange)) + { + USHORT nFlags; + pDocument->GetScenarioFlags(nScenTab,nFlags); + bIsEditable = !((nFlags & SC_SCENARIO_PROTECT) && (nFlags & SC_SCENARIO_TWOWAY)); + break; + } + nScenTab++; + } + } + } + else if (pDocument->IsScenario(nTab)) + { + // Determine if the preceding sheet is protected + SCTAB nActualTab = nTab; + do + { + nActualTab--; + } + while(pDocument->IsScenario(nActualTab)); + + if(pDocument->IsTabProtected(nActualTab)) + { + ScRange aEditRange(nCol1, nRow1, nTab, nCol2, nRow2, nTab); + if(pDocument->HasScenarioRange(nTab, aEditRange)) + { + USHORT nFlags; + pDocument->GetScenarioFlags(nTab,nFlags); + bIsEditable = !(nFlags & SC_SCENARIO_PROTECT); + } + } + } + if ( bIsEditable ) + { + if ( HasBlockMatrixFragment( nCol1, nRow1, nCol2, nRow2 ) ) + { + bIsEditable = FALSE; + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = TRUE; + } + else if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + } + else if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + return bIsEditable; +} + + +BOOL ScTable::IsSelectionEditable( const ScMarkData& rMark, + BOOL* pOnlyNotBecauseOfMatrix /* = NULL */ ) const +{ + BOOL bIsEditable = TRUE; + if ( nLockCount ) + bIsEditable = FALSE; + else if ( IsProtected() && !pDocument->IsScenario(nTab) ) + { + if((bIsEditable = !HasAttribSelection( rMark, HASATTR_PROTECTED )) != FALSE) + { + // If Sheet is protected and cells are not protected then + // check the active scenario protect flag if this area is + // in the active scenario range. + ScRangeList aRanges; + rMark.FillRangeListWithMarks( &aRanges, FALSE ); + ULONG nRangeCount = aRanges.Count(); + SCTAB nScenTab = nTab+1; + while(pDocument->IsScenario(nScenTab) && bIsEditable) + { + if(pDocument->IsActiveScenario(nScenTab)) + { + for (ULONG i=0; i<nRangeCount && bIsEditable; i++) + { + ScRange aRange = *aRanges.GetObject(i); + if(pDocument->HasScenarioRange(nScenTab, aRange)) + { + USHORT nFlags; + pDocument->GetScenarioFlags(nScenTab,nFlags); + bIsEditable = !((nFlags & SC_SCENARIO_PROTECT) && (nFlags & SC_SCENARIO_TWOWAY)); + } + } + } + nScenTab++; + } + } + } + else if (pDocument->IsScenario(nTab)) + { + // Determine if the preceding sheet is protected + SCTAB nActualTab = nTab; + do + { + nActualTab--; + } + while(pDocument->IsScenario(nActualTab)); + + if(pDocument->IsTabProtected(nActualTab)) + { + ScRangeList aRanges; + rMark.FillRangeListWithMarks( &aRanges, FALSE ); + ULONG nRangeCount = aRanges.Count(); + for (ULONG i=0; i<nRangeCount && bIsEditable; i++) + { + ScRange aRange = *aRanges.GetObject(i); + if(pDocument->HasScenarioRange(nTab, aRange)) + { + USHORT nFlags; + pDocument->GetScenarioFlags(nTab,nFlags); + bIsEditable = !(nFlags & SC_SCENARIO_PROTECT); + } + } + } + } + if ( bIsEditable ) + { + if ( HasSelectionMatrixFragment( rMark ) ) + { + bIsEditable = FALSE; + if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = TRUE; + } + else if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + } + else if ( pOnlyNotBecauseOfMatrix ) + *pOnlyNotBecauseOfMatrix = FALSE; + return bIsEditable; +} + + + +void ScTable::LockTable() +{ + ++nLockCount; +} + + +void ScTable::UnlockTable() +{ + if (nLockCount) + --nLockCount; + else + { + DBG_ERROR("UnlockTable ohne LockTable"); + } +} + + +void ScTable::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, BOOL bDeep ) const +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].MergeSelectionPattern( rState, rMark, bDeep ); +} + + +void ScTable::MergePatternArea( ScMergePatternState& rState, SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2, BOOL bDeep ) const +{ + for (SCCOL i=nCol1; i<=nCol2; i++) + aCol[i].MergePatternArea( rState, nRow1, nRow2, bDeep ); +} + + +void ScTable::MergeBlockFrame( SvxBoxItem* pLineOuter, SvxBoxInfoItem* pLineInner, ScLineFlags& rFlags, + SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) const +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + for (SCCOL i=nStartCol; i<=nEndCol; i++) + aCol[i].MergeBlockFrame( pLineOuter, pLineInner, rFlags, + nStartRow, nEndRow, (i==nStartCol), nEndCol-i ); + } +} + + +void ScTable::ApplyBlockFrame( const SvxBoxItem* pLineOuter, const SvxBoxInfoItem* pLineInner, + SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + for (SCCOL i=nStartCol; i<=nEndCol; i++) + aCol[i].ApplyBlockFrame( pLineOuter, pLineInner, + nStartRow, nEndRow, (i==nStartCol), nEndCol-i ); + } +} + + +void ScTable::ApplyPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].ApplyPattern( nRow, rAttr ); +} + + +void ScTable::ApplyPatternArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + const ScPatternAttr& rAttr ) +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + for (SCCOL i = nStartCol; i <= nEndCol; i++) + aCol[i].ApplyPatternArea(nStartRow, nEndRow, rAttr); + } +} + +void ScTable::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange, + const ScPatternAttr& rPattern, short nNewType ) +{ + SCCOL nEndCol = rRange.aEnd.Col(); + for ( SCCOL nCol = rRange.aStart.Col(); nCol <= nEndCol; nCol++ ) + { + aCol[nCol].ApplyPatternIfNumberformatIncompatible( rRange, rPattern, nNewType ); + } +} + + + +void ScTable::ApplyStyle( SCCOL nCol, SCROW nRow, const ScStyleSheet& rStyle ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].ApplyStyle( nRow, rStyle ); +} + + +void ScTable::ApplyStyleArea( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, const ScStyleSheet& rStyle ) +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + PutInOrder(nStartCol, nEndCol); + PutInOrder(nStartRow, nEndRow); + for (SCCOL i = nStartCol; i <= nEndCol; i++) + aCol[i].ApplyStyleArea(nStartRow, nEndRow, rStyle); + } +} + + +void ScTable::ApplySelectionStyle(const ScStyleSheet& rStyle, const ScMarkData& rMark) +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].ApplySelectionStyle( rStyle, rMark ); +} + + +void ScTable::ApplySelectionLineStyle( const ScMarkData& rMark, + const SvxBorderLine* pLine, BOOL bColorOnly ) +{ + if ( bColorOnly && !pLine ) + return; + + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].ApplySelectionLineStyle( rMark, pLine, bColorOnly ); +} + + +const ScStyleSheet* ScTable::GetStyle( SCCOL nCol, SCROW nRow ) const +{ + if (ValidColRow(nCol, nRow)) + return aCol[nCol].GetStyle(nRow); + else + return NULL; +} + + +const ScStyleSheet* ScTable::GetSelectionStyle( const ScMarkData& rMark, BOOL& rFound ) const +{ + rFound = FALSE; + + BOOL bEqual = TRUE; + BOOL bColFound; + + const ScStyleSheet* pStyle = NULL; + const ScStyleSheet* pNewStyle; + + for (SCCOL i=0; i<=MAXCOL && bEqual; i++) + if (rMark.HasMultiMarks(i)) + { + pNewStyle = aCol[i].GetSelectionStyle( rMark, bColFound ); + if (bColFound) + { + rFound = TRUE; + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + } + + return bEqual ? pStyle : NULL; +} + + +const ScStyleSheet* ScTable::GetAreaStyle( BOOL& rFound, SCCOL nCol1, SCROW nRow1, + SCCOL nCol2, SCROW nRow2 ) const +{ + rFound = FALSE; + + BOOL bEqual = TRUE; + BOOL bColFound; + + const ScStyleSheet* pStyle = NULL; + const ScStyleSheet* pNewStyle; + + for (SCCOL i=nCol1; i<=nCol2 && bEqual; i++) + { + pNewStyle = aCol[i].GetAreaStyle(bColFound, nRow1, nRow2); + if (bColFound) + { + rFound = TRUE; + if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) ) + bEqual = FALSE; // unterschiedliche + pStyle = pNewStyle; + } + } + + return bEqual ? pStyle : NULL; +} + + +BOOL ScTable::IsStyleSheetUsed( const ScStyleSheet& rStyle, BOOL bGatherAllStyles ) const +{ + BOOL bIsUsed = FALSE; + + for ( SCCOL i=0; i<=MAXCOL; i++ ) + { + if ( aCol[i].IsStyleSheetUsed( rStyle, bGatherAllStyles ) ) + { + if ( !bGatherAllStyles ) + return TRUE; + bIsUsed = TRUE; + } + } + + return bIsUsed; +} + + +void ScTable::StyleSheetChanged( const SfxStyleSheetBase* pStyleSheet, BOOL bRemoved, + OutputDevice* pDev, + double nPPTX, double nPPTY, + const Fraction& rZoomX, const Fraction& rZoomY ) +{ + BOOL* pUsed = new BOOL[MAXROWCOUNT]; + memset( pUsed, 0, sizeof(BOOL) * (MAXROWCOUNT) ); + + SCCOL nCol; + for (nCol=0; nCol<=MAXCOL; nCol++) + aCol[nCol].FindStyleSheet( pStyleSheet, pUsed, bRemoved ); + + BOOL bFound = FALSE; + SCROW nStart = 0, nEnd = 0; + for (SCROW i=0; i<=MAXROW; i++) + { + if (pUsed[i]) + { + if (!bFound) + { + nStart = i; + bFound = TRUE; + } + nEnd = i; + } + else if (bFound) + { + SetOptimalHeight( nStart, nEnd, 0, pDev, nPPTX, nPPTY, rZoomX, rZoomY, FALSE ); + bFound = FALSE; + } + } + if (bFound) + SetOptimalHeight( nStart, nEnd, 0, pDev, nPPTX, nPPTY, rZoomX, rZoomY, FALSE ); + + delete[] pUsed; +} + + +BOOL ScTable::ApplyFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + INT16 nFlags ) +{ + BOOL bChanged = FALSE; + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + for (SCCOL i = nStartCol; i <= nEndCol; i++) + bChanged |= aCol[i].ApplyFlags(nStartRow, nEndRow, nFlags); + return bChanged; +} + + +BOOL ScTable::RemoveFlags( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + INT16 nFlags ) +{ + BOOL bChanged = FALSE; + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + for (SCCOL i = nStartCol; i <= nEndCol; i++) + bChanged |= aCol[i].RemoveFlags(nStartRow, nEndRow, nFlags); + return bChanged; +} + + +void ScTable::SetPattern( SCCOL nCol, SCROW nRow, const ScPatternAttr& rAttr, BOOL bPutToPool ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].SetPattern( nRow, rAttr, bPutToPool ); +} + + +void ScTable::ApplyAttr( SCCOL nCol, SCROW nRow, const SfxPoolItem& rAttr ) +{ + if (ValidColRow(nCol,nRow)) + aCol[nCol].ApplyAttr( nRow, rAttr ); +} + + +void ScTable::ApplySelectionCache( SfxItemPoolCache* pCache, const ScMarkData& rMark ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].ApplySelectionCache( pCache, rMark ); +} + + +void ScTable::ChangeSelectionIndent( BOOL bIncrement, const ScMarkData& rMark ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].ChangeSelectionIndent( bIncrement, rMark ); +} + + +void ScTable::ClearSelectionItems( const USHORT* pWhich, const ScMarkData& rMark ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) + aCol[i].ClearSelectionItems( pWhich, rMark ); +} + + +// Spaltenbreiten / Zeilenhoehen + +void ScTable::SetColWidth( SCCOL nCol, USHORT nNewWidth ) +{ + if (VALIDCOL(nCol) && pColWidth) + { + if (!nNewWidth) + { +// DBG_ERROR("Spaltenbreite 0 in SetColWidth"); + nNewWidth = STD_COL_WIDTH; + } + + if ( nNewWidth != pColWidth[nCol] ) + { + nRecalcLvl++; + InitializeNoteCaptions(); + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + pDrawLayer->WidthChanged( nTab, nCol, ((long) nNewWidth) - (long) pColWidth[nCol] ); + pColWidth[nCol] = nNewWidth; + if( !--nRecalcLvl ) + SetDrawPageSize(); + } + } + else + { + DBG_ERROR("Falsche Spaltennummer oder keine Breiten"); + } +} + + +void ScTable::SetRowHeight( SCROW nRow, USHORT nNewHeight ) +{ + if (VALIDROW(nRow) && pRowHeight) + { + if (!nNewHeight) + { + DBG_ERROR("Zeilenhoehe 0 in SetRowHeight"); + nNewHeight = ScGlobal::nStdRowHeight; + } + + USHORT nOldHeight = pRowHeight->GetValue(nRow); + if ( nNewHeight != nOldHeight ) + { + nRecalcLvl++; + InitializeNoteCaptions(); + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + pDrawLayer->HeightChanged( nTab, nRow, ((long) nNewHeight) - (long) nOldHeight ); + pRowHeight->SetValue( nRow, nNewHeight); + if( !--nRecalcLvl ) + SetDrawPageSize(); + } + } + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Hoehen"); + } +} + + +BOOL ScTable::SetRowHeightRange( SCROW nStartRow, SCROW nEndRow, USHORT nNewHeight, + double /* nPPTX */, double nPPTY ) +{ + BOOL bChanged = FALSE; + if (VALIDROW(nStartRow) && VALIDROW(nEndRow) && pRowHeight) + { + nRecalcLvl++; + InitializeNoteCaptions(); + if (!nNewHeight) + { + DBG_ERROR("Zeilenhoehe 0 in SetRowHeight"); + nNewHeight = ScGlobal::nStdRowHeight; + } + + long nNewPix = (long) ( nNewHeight * nPPTY ); + + BOOL bSingle = FALSE; // TRUE = process every row for its own + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + if (pDrawLayer->HasObjectsInRows( nTab, nStartRow, nEndRow )) + bSingle = TRUE; + + if (bSingle) + { + size_t nIndex; + SCROW nRegionEndRow; + USHORT nOldHeight = pRowHeight->GetValue( nStartRow, nIndex, nRegionEndRow); + if (nNewHeight == nOldHeight && nEndRow <= nRegionEndRow) + bSingle = FALSE; // no difference in this range + } + if (bSingle) + { + if (nEndRow-nStartRow < 20) + { + // Whether new pixel size will differ from old pixel size in any row. + ScCompressedArrayIterator< SCROW, USHORT> aIter( *pRowHeight, + nStartRow, nEndRow); + do + { + if (*aIter != nNewHeight) + bChanged = (nNewPix != (long) (*aIter * nPPTY)); + } while (!bChanged && aIter.NextRange()); + + /* #i94028# #i94991# If drawing objects are involved, each row + has to be changed for its own, because each call to + ScDrawLayer::HeightChanged expects correct row heights + above passed row in the document. Cannot use array iterator + because array changes in every cycle. */ + if( pDrawLayer ) + { + for( SCROW nRow = nStartRow; nRow <= nEndRow ; ++nRow ) + { + pDrawLayer->HeightChanged( nTab, nRow, + ((long) nNewHeight) - ((long) pRowHeight->GetValue( nRow ))); + pRowHeight->SetValue( nRow, nNewHeight ); + } + } + else + pRowHeight->SetValue( nStartRow, nEndRow, nNewHeight); + } + else + { + SCROW nMid = (nStartRow+nEndRow) / 2; + if (SetRowHeightRange( nStartRow, nMid, nNewHeight, 1.0, 1.0 )) + bChanged = TRUE; + if (SetRowHeightRange( nMid+1, nEndRow, nNewHeight, 1.0, 1.0 )) + bChanged = TRUE; + } + } + else + { + if (pDrawLayer) + { + unsigned long nOldHeights = pRowHeight->SumValues( nStartRow, nEndRow); + // FIXME: should we test for overflows? + long nHeightDif = (long) (unsigned long) nNewHeight * + (nEndRow - nStartRow + 1) - nOldHeights; + pDrawLayer->HeightChanged( nTab, nEndRow, nHeightDif ); + } + // Whether new pixel size will differ from old pixel size in any row. + ScCompressedArrayIterator< SCROW, USHORT> aIter( *pRowHeight, + nStartRow, nEndRow); + do + { + if (*aIter != nNewHeight) + bChanged = (nNewPix != (long) (*aIter * nPPTY)); + } while (!bChanged && aIter.NextRange()); + pRowHeight->SetValue( nStartRow, nEndRow, nNewHeight); + } + if( !--nRecalcLvl ) + SetDrawPageSize(); + } + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Hoehen"); + } + + return bChanged; +} + + +void ScTable::SetManualHeight( SCROW nStartRow, SCROW nEndRow, BOOL bManual ) +{ + if (VALIDROW(nStartRow) && VALIDROW(nEndRow) && pRowFlags) + { + if (bManual) + pRowFlags->OrValue( nStartRow, nEndRow, CR_MANUALSIZE); + else + pRowFlags->AndValue( nStartRow, nEndRow, sal::static_int_cast<BYTE>(~CR_MANUALSIZE)); + } + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Zeilenflags"); + } +} + + +USHORT ScTable::GetColWidth( SCCOL nCol ) const +{ + DBG_ASSERT(VALIDCOL(nCol),"Falsche Spaltennummer"); + + if (VALIDCOL(nCol) && pColFlags && pColWidth) + { + if ( pColFlags[nCol] & CR_HIDDEN ) + return 0; + else + return pColWidth[nCol]; + } + else + return (USHORT) STD_COL_WIDTH; +} + + +USHORT ScTable::GetOriginalWidth( SCCOL nCol ) const // immer die eingestellte +{ + DBG_ASSERT(VALIDCOL(nCol),"Falsche Spaltennummer"); + + if (VALIDCOL(nCol) && pColWidth) + return pColWidth[nCol]; + else + return (USHORT) STD_COL_WIDTH; +} + + +USHORT ScTable::GetCommonWidth( SCCOL nEndCol ) const +{ + // get the width that is used in the largest continuous column range (up to nEndCol) + + if ( !ValidCol(nEndCol) ) + { + DBG_ERROR("wrong column"); + nEndCol = MAXCOL; + } + + USHORT nMaxWidth = 0; + USHORT nMaxCount = 0; + USHORT nRangeStart = 0; + while ( nRangeStart <= nEndCol ) + { + // skip hidden columns + while ( nRangeStart <= nEndCol && (pColFlags[nRangeStart] & CR_HIDDEN) ) + ++nRangeStart; + if ( nRangeStart <= nEndCol ) + { + USHORT nThisCount = 0; + USHORT nThisWidth = pColWidth[nRangeStart]; + USHORT nRangeEnd = nRangeStart; + while ( nRangeEnd <= nEndCol && pColWidth[nRangeEnd] == nThisWidth ) + { + ++nThisCount; + ++nRangeEnd; + + // skip hidden columns + while ( nRangeEnd <= nEndCol && (pColFlags[nRangeEnd] & CR_HIDDEN) ) + ++nRangeEnd; + } + + if ( nThisCount > nMaxCount ) + { + nMaxCount = nThisCount; + nMaxWidth = nThisWidth; + } + + nRangeStart = nRangeEnd; // next range + } + } + + return nMaxWidth; +} + + +USHORT ScTable::GetRowHeight( SCROW nRow ) const +{ + DBG_ASSERT(VALIDROW(nRow),"Falsche Zeilennummer"); + + if (VALIDROW(nRow) && pRowFlags && pRowHeight) + { + if ( pRowFlags->GetValue(nRow) & CR_HIDDEN ) + return 0; + else + return pRowHeight->GetValue(nRow); + } + else + return (USHORT) ScGlobal::nStdRowHeight; +} + + +ULONG ScTable::GetRowHeight( SCROW nStartRow, SCROW nEndRow ) const +{ + DBG_ASSERT(VALIDROW(nStartRow) && VALIDROW(nEndRow),"Falsche Zeilennummer"); + + if (VALIDROW(nStartRow) && VALIDROW(nEndRow) && pRowFlags && pRowHeight) + { + return pRowFlags->SumCoupledArrayForCondition( nStartRow, nEndRow, + CR_HIDDEN, 0, *pRowHeight); + } + else + return (ULONG) ((nEndRow - nStartRow + 1) * ScGlobal::nStdRowHeight); +} + + +ULONG ScTable::GetScaledRowHeight( SCROW nStartRow, SCROW nEndRow, double fScale ) const +{ + DBG_ASSERT(VALIDROW(nStartRow) && VALIDROW(nEndRow),"Falsche Zeilennummer"); + + if (VALIDROW(nStartRow) && VALIDROW(nEndRow) && pRowFlags && pRowHeight) + { + return pRowFlags->SumScaledCoupledArrayForCondition( nStartRow, + nEndRow, CR_HIDDEN, 0, *pRowHeight, fScale); + } + else + return (ULONG) ((nEndRow - nStartRow + 1) * ScGlobal::nStdRowHeight * fScale); +} + + +USHORT ScTable::GetOriginalHeight( SCROW nRow ) const // non-0 even if hidden +{ + DBG_ASSERT(VALIDROW(nRow),"wrong row number"); + + if (VALIDROW(nRow) && pRowHeight) + return pRowHeight->GetValue(nRow); + else + return (USHORT) ScGlobal::nStdRowHeight; +} + + +// Spalten-/Zeilen-Flags + + +SCROW ScTable::GetHiddenRowCount( SCROW nRow ) const +{ + SCROW nEndRow = nRow; + if ( pRowFlags ) + { + nEndRow = pRowFlags->GetBitStateEnd( nRow, CR_HIDDEN, CR_HIDDEN); + if (ValidRow(nEndRow)) + ++nEndRow; + else + nEndRow = nRow; + } + return nEndRow - nRow; +} + + +//! ShowRows / DBShowRows zusammenfassen + +void ScTable::ShowCol(SCCOL nCol, BOOL bShow) +{ + if (VALIDCOL(nCol) && pColFlags) + { + BOOL bWasVis = ( pColFlags[nCol] & CR_HIDDEN ) == 0; + if (bWasVis != bShow) + { + nRecalcLvl++; + InitializeNoteCaptions(); + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + if (bShow) + pDrawLayer->WidthChanged( nTab, nCol, (long) pColWidth[nCol] ); + else + pDrawLayer->WidthChanged( nTab, nCol, -(long) pColWidth[nCol] ); + } + + if (bShow) + pColFlags[nCol] &= ~CR_HIDDEN; + else + pColFlags[nCol] |= CR_HIDDEN; + if( !--nRecalcLvl ) + SetDrawPageSize(); + + ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); + if ( pCharts ) + pCharts->SetRangeDirty(ScRange( nCol, 0, nTab, nCol, MAXROW, nTab )); + } + } + else + { + DBG_ERROR("Falsche Spaltennummer oder keine Flags"); + } +} + + +void ScTable::ShowRow(SCROW nRow, BOOL bShow) +{ + if (VALIDROW(nRow) && pRowFlags) + { + BYTE nFlags = pRowFlags->GetValue(nRow); + BOOL bWasVis = ( nFlags & CR_HIDDEN ) == 0; + if (bWasVis != bShow) + { + nRecalcLvl++; + InitializeNoteCaptions(); + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + if (bShow) + pDrawLayer->HeightChanged( nTab, nRow, (long) pRowHeight->GetValue(nRow) ); + else + pDrawLayer->HeightChanged( nTab, nRow, -(long) pRowHeight->GetValue(nRow) ); + } + + if (bShow) + pRowFlags->SetValue( nRow, nFlags & ~(CR_HIDDEN | CR_FILTERED)); + else + pRowFlags->SetValue( nRow, nFlags | CR_HIDDEN); + if( !--nRecalcLvl ) + SetDrawPageSize(); + + ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); + if ( pCharts ) + pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, MAXCOL, nRow, nTab )); + } + } + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Flags"); + } +} + + +void ScTable::DBShowRow(SCROW nRow, BOOL bShow) +{ + if (VALIDROW(nRow) && pRowFlags) + { + BYTE nFlags = pRowFlags->GetValue(nRow); + BOOL bWasVis = ( nFlags & CR_HIDDEN ) == 0; + nRecalcLvl++; + InitializeNoteCaptions(); + if (bWasVis != bShow) + { + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + if (bShow) + pDrawLayer->HeightChanged( nTab, nRow, (long) pRowHeight->GetValue(nRow) ); + else + pDrawLayer->HeightChanged( nTab, nRow, -(long) pRowHeight->GetValue(nRow) ); + } + } + + // Filter-Flag immer setzen, auch wenn Hidden unveraendert + if (bShow) + pRowFlags->SetValue( nRow, nFlags & ~(CR_HIDDEN | CR_FILTERED)); + else + pRowFlags->SetValue( nRow, nFlags | (CR_HIDDEN | CR_FILTERED)); + if( !--nRecalcLvl ) + SetDrawPageSize(); + + if (bWasVis != bShow) + { + ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); + if ( pCharts ) + pCharts->SetRangeDirty(ScRange( 0, nRow, nTab, MAXCOL, nRow, nTab )); + + if (pOutlineTable) + UpdateOutlineRow( nRow, nRow, bShow ); + } + } + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Flags"); + } +} + + +void ScTable::DBShowRows(SCROW nRow1, SCROW nRow2, BOOL bShow) +{ + SCROW nStartRow = nRow1; + nRecalcLvl++; + InitializeNoteCaptions(); + while (nStartRow <= nRow2) + { + BYTE nOldFlag = pRowFlags->GetValue(nStartRow) & CR_HIDDEN; + SCROW nEndRow = pRowFlags->GetBitStateEnd( nStartRow, CR_HIDDEN, nOldFlag); + if (nEndRow > nRow2) + nEndRow = nRow2; + + BOOL bWasVis = ( nOldFlag == 0 ); + BOOL bChanged = ( bWasVis != bShow ); + if ( bChanged ) + { + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + long nHeight = (long) pRowHeight->SumValues( nStartRow, nEndRow); + if (bShow) + pDrawLayer->HeightChanged( nTab, nStartRow, nHeight ); + else + pDrawLayer->HeightChanged( nTab, nStartRow, -nHeight ); + } + } + + if (bShow) + pRowFlags->AndValue( nStartRow, nEndRow, sal::static_int_cast<BYTE>(~(CR_HIDDEN | CR_FILTERED)) ); + else + pRowFlags->OrValue( nStartRow, nEndRow, (CR_HIDDEN | CR_FILTERED)); + + if ( bChanged ) + { + ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); + if ( pCharts ) + pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, MAXCOL, nEndRow, nTab )); + } + + nStartRow = nEndRow + 1; + } + + // #i12341# For Show/Hide rows, the outlines are updated separately from the outside. + // For filtering, the changes aren't visible to the caller, so UpdateOutlineRow has + // to be done here. + if (pOutlineTable) + UpdateOutlineRow( nRow1, nRow2, bShow ); + + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +void ScTable::ShowRows(SCROW nRow1, SCROW nRow2, BOOL bShow) +{ + SCROW nStartRow = nRow1; + nRecalcLvl++; + InitializeNoteCaptions(); + while (nStartRow <= nRow2) + { + BYTE nOldFlag = pRowFlags->GetValue(nStartRow) & CR_HIDDEN; + SCROW nEndRow = pRowFlags->GetBitStateEnd( nStartRow, CR_HIDDEN, nOldFlag); + if (nEndRow > nRow2) + nEndRow = nRow2; + + BOOL bWasVis = ( nOldFlag == 0 ); + BOOL bChanged = ( bWasVis != bShow ); + if ( bChanged ) + { + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if (pDrawLayer) + { + long nHeight = (long) pRowHeight->SumValues( nStartRow, nEndRow); + if (bShow) + pDrawLayer->HeightChanged( nTab, nStartRow, nHeight ); + else + pDrawLayer->HeightChanged( nTab, nStartRow, -nHeight ); + } + } + + if (bShow) + pRowFlags->AndValue( nStartRow, nEndRow, sal::static_int_cast<BYTE>(~(CR_HIDDEN | CR_FILTERED)) ); + else + pRowFlags->OrValue( nStartRow, nEndRow, CR_HIDDEN); + + if ( bChanged ) + { + ScChartListenerCollection* pCharts = pDocument->GetChartListenerCollection(); + if ( pCharts ) + pCharts->SetRangeDirty(ScRange( 0, nStartRow, nTab, MAXCOL, nEndRow, nTab )); + } + + nStartRow = nEndRow + 1; + } + if( !--nRecalcLvl ) + SetDrawPageSize(); +} + + +BOOL ScTable::IsFiltered(SCROW nRow) const +{ + if (VALIDROW(nRow) && pRowFlags) + return ( pRowFlags->GetValue(nRow) & CR_FILTERED ) != 0; + + DBG_ERROR("Falsche Zeilennummer oder keine Flags"); + return FALSE; +} + + +void ScTable::SetColFlags( SCCOL nCol, BYTE nNewFlags ) +{ + if (VALIDCOL(nCol) && pColFlags) + pColFlags[nCol] = nNewFlags; + else + { + DBG_ERROR("Falsche Spaltennummer oder keine Flags"); + } +} + + +void ScTable::SetRowFlags( SCROW nRow, BYTE nNewFlags ) +{ + if (VALIDROW(nRow) && pRowFlags) + pRowFlags->SetValue( nRow, nNewFlags); + else + { + DBG_ERROR("Falsche Zeilennummer oder keine Flags"); + } +} + + +void ScTable::SetRowFlags( SCROW nStartRow, SCROW nEndRow, BYTE nNewFlags ) +{ + if (VALIDROW(nStartRow) && VALIDROW(nEndRow) && pRowFlags) + pRowFlags->SetValue( nStartRow, nEndRow, nNewFlags); + else + { + DBG_ERROR("Falsche Zeilennummer(n) oder keine Flags"); + } +} + + +BYTE ScTable::GetColFlags( SCCOL nCol ) const +{ + if (VALIDCOL(nCol) && pColFlags) + return pColFlags[nCol]; + else + return 0; +} + + +BYTE ScTable::GetRowFlags( SCROW nRow ) const +{ + if (VALIDROW(nRow) && pRowFlags) + return pRowFlags->GetValue(nRow); + else + return 0; +} + + +SCROW ScTable::GetLastFlaggedRow() const +{ + if ( !pRowFlags ) + return 0; + + SCROW nLastFound = pRowFlags->GetLastAnyBitAccess( 0, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + return ValidRow(nLastFound) ? nLastFound : 0; +} + + +SCCOL ScTable::GetLastChangedCol() const +{ + if ( !pColFlags ) + return 0; + + SCCOL nLastFound = 0; + for (SCCOL nCol = 1; nCol <= MAXCOL; nCol++) + if ((pColFlags[nCol] & ~CR_PAGEBREAK) || (pColWidth[nCol] != STD_COL_WIDTH)) + nLastFound = nCol; + + return nLastFound; +} + + +SCROW ScTable::GetLastChangedRow() const +{ + if ( !pRowFlags ) + return 0; + + SCROW nLastFlags = pRowFlags->GetLastAnyBitAccess( 0, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + if (!ValidRow(nLastFlags)) + nLastFlags = 0; + + SCROW nLastHeight = pRowHeight->GetLastUnequalAccess( 0, ScGlobal::nStdRowHeight); + if (!ValidRow(nLastHeight)) + nLastHeight = 0; + + return std::max( nLastFlags, nLastHeight); +} + + +BOOL ScTable::UpdateOutlineCol( SCCOL nStartCol, SCCOL nEndCol, BOOL bShow ) +{ + if (pOutlineTable && pColFlags) + { + ScBitMaskCompressedArray< SCCOLROW, BYTE> aArray( MAXCOL, pColFlags, MAXCOLCOUNT); + return pOutlineTable->GetColArray()->ManualAction( nStartCol, nEndCol, bShow, aArray ); + } + else + return FALSE; +} + + +BOOL ScTable::UpdateOutlineRow( SCROW nStartRow, SCROW nEndRow, BOOL bShow ) +{ + if (pOutlineTable && pRowFlags) + return pOutlineTable->GetRowArray()->ManualAction( nStartRow, nEndRow, bShow, *pRowFlags ); + else + return FALSE; +} + + +void ScTable::ExtendHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 ) +{ + if (pColFlags) + { + while ( rX1>0 ? (pColFlags[rX1-1] & CR_HIDDEN) : FALSE ) + --rX1; + while ( rX2<MAXCOL ? (pColFlags[rX2+1] & CR_HIDDEN) : FALSE ) + ++rX2; + } + if (pRowFlags) + { + if (rY1 > 0) + { + SCROW nStartRow = pRowFlags->GetBitStateStart( rY1-1, CR_HIDDEN, CR_HIDDEN); + if (ValidRow(nStartRow)) + rY1 = nStartRow; + } + if (rY2 < MAXROW) + { + SCROW nEndRow = pRowFlags->GetBitStateEnd( rY2+1, CR_HIDDEN, CR_HIDDEN); + if (ValidRow(nEndRow)) + rY2 = nEndRow; + } + } +} + + +void ScTable::StripHidden( SCCOL& rX1, SCROW& rY1, SCCOL& rX2, SCROW& rY2 ) +{ + if (pColFlags) + { + while ( rX2>rX1 && (pColFlags[rX2] & CR_HIDDEN) ) + --rX2; + while ( rX2>rX1 && (pColFlags[rX1] & CR_HIDDEN) ) + ++rX1; + } + if (pRowFlags) + { + if (rY1 < rY2) + { + SCROW nStartRow = pRowFlags->GetBitStateStart( rY2, CR_HIDDEN, CR_HIDDEN); + if (ValidRow(nStartRow) && nStartRow >= rY1) + rY2 = nStartRow; + } + if (rY1 < rY2) + { + SCROW nEndRow = pRowFlags->GetBitStateEnd( rY1, CR_HIDDEN, CR_HIDDEN); + if (ValidRow(nEndRow) && nEndRow <= rY2) + rY1 = nEndRow; + } + } +} + + +// Auto-Outline + +template< typename T > +short DiffSign( T a, T b ) +{ + return (a<b) ? -1 : + (a>b) ? 1 : 0; +} + + +void ScTable::DoAutoOutline( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow ) +{ + BOOL bSizeChanged = FALSE; + BOOL bMissed = FALSE; + + SCCOL nCol; + SCROW nRow; + SCROW i; + BOOL bFound; + ScOutlineArray* pArray; + ScBaseCell* pCell; + ScRange aRef; +/* ScPatternAttr aBoldPattern( pDocument->GetPool() ); //! spezielle Format-Vorlage + aBoldPattern.GetItemSet().Put( SvxWeightItem( WEIGHT_BOLD ) ); +*/ + + StartOutlineTable(); + + // Zeilen + + SCROW nCount = nEndRow-nStartRow+1; + BOOL* pUsed = new BOOL[nCount]; + for (i=0; i<nCount; i++) + pUsed[i] = FALSE; + for (nCol=nStartCol; nCol<=nEndCol; nCol++) + if (!aCol[nCol].IsEmptyData()) + aCol[nCol].FindUsed( nStartRow, nEndRow, pUsed ); + + pArray = pOutlineTable->GetRowArray(); + for (nRow=nStartRow; nRow<=nEndRow; nRow++) + if (pUsed[nRow-nStartRow]) + { + bFound = FALSE; + for (nCol=nStartCol; nCol<=nEndCol && !bFound; nCol++) + if (!aCol[nCol].IsEmptyData()) + { + pCell = aCol[nCol].GetCell( nRow ); + if (pCell) + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pCell)->HasRefListExpressibleAsOneReference( aRef )) + if ( aRef.aStart.Col() == nCol && aRef.aEnd.Col() == nCol && + aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && + DiffSign( aRef.aStart.Row(), nRow ) == + DiffSign( aRef.aEnd.Row(), nRow ) ) + { + if (pArray->Insert( aRef.aStart.Row(), aRef.aEnd.Row(), bSizeChanged )) + { +// ApplyPatternArea( nStartCol, nRow, nEndCol, nRow, aBoldPattern ); + bFound = TRUE; + } + else + bMissed = TRUE; + } + } + } + + delete[] pUsed; + + // Spalten + + pArray = pOutlineTable->GetColArray(); + for (nCol=nStartCol; nCol<=nEndCol; nCol++) + { + if (!aCol[nCol].IsEmptyData()) + { + bFound = FALSE; + ScColumnIterator aIter( &aCol[nCol], nStartRow, nEndRow ); + while ( aIter.Next( nRow, pCell ) && !bFound ) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pCell)->HasRefListExpressibleAsOneReference( aRef )) + if ( aRef.aStart.Row() == nRow && aRef.aEnd.Row() == nRow && + aRef.aStart.Tab() == nTab && aRef.aEnd.Tab() == nTab && + DiffSign( aRef.aStart.Col(), nCol ) == + DiffSign( aRef.aEnd.Col(), nCol ) ) + { + if (pArray->Insert( aRef.aStart.Col(), aRef.aEnd.Col(), bSizeChanged )) + { +// ApplyPatternArea( nCol, nStartRow, nCol, nEndRow, aBoldPattern ); + bFound = TRUE; + } + else + bMissed = TRUE; + } + } + } + } +} + + // CopyData - fuer Query in anderen Bereich + +void ScTable::CopyData( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + SCCOL nDestCol, SCROW nDestRow, SCTAB nDestTab ) +{ + //! wenn fuer mehrere Zeilen benutzt, nach Spalten optimieren! + + ScAddress aSrc( nStartCol, nStartRow, nTab ); + ScAddress aDest( nDestCol, nDestRow, nDestTab ); + ScRange aRange( aSrc, aDest ); + BOOL bThisTab = ( nDestTab == nTab ); + SCROW nDestY = nDestRow; + for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) + { + aSrc.SetRow( nRow ); + aDest.SetRow( nDestY ); + SCCOL nDestX = nDestCol; + for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) + { + aSrc.SetCol( nCol ); + aDest.SetCol( nDestX ); + ScBaseCell* pCell = GetCell( nCol, nRow ); + if (pCell) + { + pCell = pCell->CloneWithoutNote( *pDocument ); + if (pCell->GetCellType() == CELLTYPE_FORMULA) + { + ((ScFormulaCell*)pCell)->UpdateReference( URM_COPY, aRange, + ((SCsCOL) nDestCol) - ((SCsCOL) nStartCol), + ((SCsROW) nDestRow) - ((SCsROW) nStartRow), + ((SCsTAB) nDestTab) - ((SCsTAB) nTab) ); + ((ScFormulaCell*)pCell)->aPos = aDest; + } + } + if (bThisTab) + { + PutCell( nDestX, nDestY, pCell ); + SetPattern( nDestX, nDestY, *GetPattern( nCol, nRow ), TRUE ); + } + else + { + pDocument->PutCell( aDest, pCell ); + pDocument->SetPattern( aDest, *GetPattern( nCol, nRow ), TRUE ); + } + + ++nDestX; + } + ++nDestY; + } +} + + +BOOL ScTable::RefVisible(ScFormulaCell* pCell) +{ + ScRange aRef; + + if (pCell->HasOneReference(aRef)) + { + if (aRef.aStart.Col()==aRef.aEnd.Col() && aRef.aStart.Tab()==aRef.aEnd.Tab() && pRowFlags) + { + // while ((value & CR_FILTERED) == CR_FILTERED) + // most times will be faster than + // while ((value & CR_FILTERED) == 0) + SCROW nEndRow = pRowFlags->GetBitStateEnd( aRef.aStart.Row(), + CR_FILTERED, CR_FILTERED); + if (!ValidRow(nEndRow) || nEndRow < aRef.aEnd.Row()) + return TRUE; // at least partly visible + return FALSE; // completely unvisible + } + } + + return TRUE; // irgendwie anders +} + + +void ScTable::GetUpperCellString(SCCOL nCol, SCROW nRow, String& rStr) +{ + GetInputString(nCol, nRow, rStr); + rStr.EraseTrailingChars(); + rStr.EraseLeadingChars(); + ScGlobal::pCharClass->toUpper(rStr); +} + + +// Berechnen der Groesse der Tabelle und setzen der Groesse an der DrawPage + +void ScTable::SetDrawPageSize(bool bResetStreamValid) +{ + ScDrawLayer* pDrawLayer = pDocument->GetDrawLayer(); + if( pDrawLayer ) + { + long x = GetColOffset( MAXCOL + 1 ); + long y = GetRowOffset( MAXROW + 1 ); + x = (long) ((double) x * HMM_PER_TWIPS); + y = (long) ((double) y * HMM_PER_TWIPS); + + if ( IsLayoutRTL() ) // IsNegativePage + x = -x; + + pDrawLayer->SetPageSize( static_cast<sal_uInt16>(nTab), Size( x, y ) ); + } + + // #i102616# actions that modify the draw page size count as sheet modification + // (exception: InitDrawLayer) + if (bResetStreamValid && IsStreamValid()) + SetStreamValid(FALSE); +} + + +ULONG ScTable::GetRowOffset( SCROW nRow ) const +{ + ULONG n = 0; + if ( pRowFlags && pRowHeight ) + { + if (nRow == 0) + return 0; + else if (nRow == 1) + return GetRowHeight(0); + + n = pRowFlags->SumCoupledArrayForCondition( 0, nRow-1, CR_HIDDEN, 0, + *pRowHeight); +#ifdef DBG_UTIL + if (n == ::std::numeric_limits<unsigned long>::max()) + DBG_ERRORFILE("ScTable::GetRowOffset: row heights overflow"); +#endif + } + else + { + DBG_ERROR("GetRowOffset: Daten fehlen"); + } + return n; +} + + +ULONG ScTable::GetColOffset( SCCOL nCol ) const +{ + ULONG n = 0; + if ( pColFlags && pColWidth ) + { + SCCOL i; + BYTE* pFlags = pColFlags; + USHORT* pWidth = pColWidth; + for( i = 0; i < nCol; i++, pFlags++, pWidth++ ) + if( !( *pFlags & CR_HIDDEN ) ) + n += *pWidth; + } + else + { + DBG_ERROR("GetColumnOffset: Daten fehlen"); + } + return n; +} + diff --git a/sc/source/core/data/table3.cxx b/sc/source/core/data/table3.cxx new file mode 100644 index 000000000000..01dc74f8644e --- /dev/null +++ b/sc/source/core/data/table3.cxx @@ -0,0 +1,1916 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table3.cxx,v $ + * $Revision: 1.30.128.1 $ + * + * 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 <rtl/math.hxx> +#include <unotools/textsearch.hxx> +#include <svtools/zforlist.hxx> +#include <unotools/charclass.hxx> +#include <unotools/collatorwrapper.hxx> +#include <com/sun/star/i18n/CollatorOptions.hpp> +#include <stdlib.h> +#include <unotools/transliterationwrapper.hxx> + +#include "table.hxx" +#include "scitems.hxx" +#include "collect.hxx" +#include "attrib.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "globstr.hrc" +#include "global.hxx" +#include "stlpool.hxx" +#include "compiler.hxx" +#include "patattr.hxx" +#include "subtotal.hxx" +#include "docoptio.hxx" +#include "markdata.hxx" +#include "rangelst.hxx" +#include "attarray.hxx" +#include "userlist.hxx" +#include "progress.hxx" +#include "cellform.hxx" +#include "postit.hxx" + +#include <vector> + +// STATIC DATA ----------------------------------------------------------- + +const USHORT nMaxSorts = 3; // maximale Anzahl Sortierkriterien in aSortParam + +struct ScSortInfo +{ + ScBaseCell* pCell; + SCCOLROW nOrg; + DECL_FIXEDMEMPOOL_NEWDEL( ScSortInfo ); +}; +const USHORT nMemPoolSortInfo = (0x8000 - 64) / sizeof(ScSortInfo); +IMPL_FIXEDMEMPOOL_NEWDEL( ScSortInfo, nMemPoolSortInfo, nMemPoolSortInfo ) + +// END OF STATIC DATA ----------------------------------------------------- + + +class ScSortInfoArray +{ +private: + ScSortInfo** pppInfo[nMaxSorts]; + SCSIZE nCount; + SCCOLROW nStart; + USHORT nUsedSorts; + +public: + ScSortInfoArray( USHORT nSorts, SCCOLROW nInd1, SCCOLROW nInd2 ) : + nCount( nInd2 - nInd1 + 1 ), nStart( nInd1 ), + nUsedSorts( Min( nSorts, nMaxSorts ) ) + { + for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ ) + { + ScSortInfo** ppInfo = new ScSortInfo* [nCount]; + for ( SCSIZE j = 0; j < nCount; j++ ) + ppInfo[j] = new ScSortInfo; + pppInfo[nSort] = ppInfo; + } + } + ~ScSortInfoArray() + { + for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ ) + { + ScSortInfo** ppInfo = pppInfo[nSort]; + for ( SCSIZE j = 0; j < nCount; j++ ) + delete ppInfo[j]; + delete [] ppInfo; + } + } + ScSortInfo* Get( USHORT nSort, SCCOLROW nInd ) + { return (pppInfo[nSort])[ nInd - nStart ]; } + void Swap( SCCOLROW nInd1, SCCOLROW nInd2 ) + { + SCSIZE n1 = static_cast<SCSIZE>(nInd1 - nStart); + SCSIZE n2 = static_cast<SCSIZE>(nInd2 - nStart); + for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ ) + { + ScSortInfo** ppInfo = pppInfo[nSort]; + ScSortInfo* pTmp = ppInfo[n1]; + ppInfo[n1] = ppInfo[n2]; + ppInfo[n2] = pTmp; + } + } + USHORT GetUsedSorts() { return nUsedSorts; } + ScSortInfo** GetFirstArray() { return pppInfo[0]; } + SCCOLROW GetStart() { return nStart; } + SCSIZE GetCount() { return nCount; } +}; + +ScSortInfoArray* ScTable::CreateSortInfoArray( SCCOLROW nInd1, SCCOLROW nInd2 ) +{ + USHORT nUsedSorts = 1; + while ( nUsedSorts < nMaxSorts && aSortParam.bDoSort[nUsedSorts] ) + nUsedSorts++; + ScSortInfoArray* pArray = new ScSortInfoArray( nUsedSorts, nInd1, nInd2 ); + if ( aSortParam.bByRow ) + { + for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ ) + { + SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]); + ScColumn* pCol = &aCol[nCol]; + for ( SCROW nRow = nInd1; nRow <= nInd2; nRow++ ) + { +//2do: FillSortInfo an ScColumn und Array abklappern statt Search in GetCell + ScSortInfo* pInfo = pArray->Get( nSort, nRow ); + pInfo->pCell = pCol->GetCell( nRow ); + pInfo->nOrg = nRow; + } + } + } + else + { + for ( USHORT nSort = 0; nSort < nUsedSorts; nSort++ ) + { + SCROW nRow = aSortParam.nField[nSort]; + for ( SCCOL nCol = static_cast<SCCOL>(nInd1); + nCol <= static_cast<SCCOL>(nInd2); nCol++ ) + { + ScSortInfo* pInfo = pArray->Get( nSort, nCol ); + pInfo->pCell = GetCell( nCol, nRow ); + pInfo->nOrg = nCol; + } + } + } + return pArray; +} + + +BOOL ScTable::IsSortCollatorGlobal() const +{ + return pSortCollator == ScGlobal::GetCollator() || + pSortCollator == ScGlobal::GetCaseCollator(); +} + + +void ScTable::InitSortCollator( const ScSortParam& rPar ) +{ + if ( rPar.aCollatorLocale.Language.getLength() ) + { + if ( !pSortCollator || IsSortCollatorGlobal() ) + pSortCollator = new CollatorWrapper( pDocument->GetServiceManager() ); + pSortCollator->loadCollatorAlgorithm( rPar.aCollatorAlgorithm, + rPar.aCollatorLocale, (rPar.bCaseSens ? 0 : SC_COLLATOR_IGNORES) ); + } + else + { // SYSTEM + DestroySortCollator(); + pSortCollator = (rPar.bCaseSens ? ScGlobal::GetCaseCollator() : + ScGlobal::GetCollator()); + } +} + + +void ScTable::DestroySortCollator() +{ + if ( pSortCollator ) + { + if ( !IsSortCollatorGlobal() ) + delete pSortCollator; + pSortCollator = NULL; + } +} + + +void ScTable::SortReorder( ScSortInfoArray* pArray, ScProgress& rProgress ) +{ + BOOL bByRow = aSortParam.bByRow; + SCSIZE nCount = pArray->GetCount(); + ScSortInfo** ppInfo = pArray->GetFirstArray(); + // hngngn.. Win16 legacy? Table has ULONG count but can only be initialized using USHORT :-/ + // FIXME: use std::vector instead, would be better anyway (type safe) + USHORT nArghl = (nCount > USHRT_MAX ? USHRT_MAX : static_cast<USHORT>(nCount)); + Table aTable( nArghl ); + SCSIZE nPos; + for ( nPos = 0; nPos < nCount; nPos++ ) + { + aTable.Insert( ppInfo[nPos]->nOrg, (void*) ppInfo[nPos] ); + } + SCCOLROW nDest = pArray->GetStart(); + for ( nPos = 0; nPos < nCount; nPos++, nDest++ ) + { + SCCOLROW nOrg = ppInfo[nPos]->nOrg; + if ( nDest != nOrg ) + { + if ( bByRow ) + SwapRow( nDest, nOrg ); + else + SwapCol( static_cast<SCCOL>(nDest), static_cast<SCCOL>(nOrg) ); + // neue Position des weggeswapten eintragen + ScSortInfo* p = ppInfo[nPos]; + p->nOrg = nDest; + p = (ScSortInfo*) aTable.Replace( nDest, (void*) p ); + p->nOrg = nOrg; + p = (ScSortInfo*) aTable.Replace( nOrg, (void*) p ); + DBG_ASSERT( p == ppInfo[nPos], "SortReorder: nOrg MisMatch" ); + } + rProgress.SetStateOnPercent( nPos ); + } +} + +short ScTable::CompareCell( USHORT nSort, + ScBaseCell* pCell1, SCCOL nCell1Col, SCROW nCell1Row, + ScBaseCell* pCell2, SCCOL nCell2Col, SCROW nCell2Row ) +{ + short nRes = 0; + + CellType eType1 = CELLTYPE_NONE, eType2 = CELLTYPE_NONE; + if (pCell1) + { + eType1 = pCell1->GetCellType(); + if (eType1 == CELLTYPE_NOTE) + pCell1 = NULL; + } + if (pCell2) + { + eType2 = pCell2->GetCellType(); + if (eType2 == CELLTYPE_NOTE) + pCell2 = NULL; + } + + if (pCell1) + { + if (pCell2) + { + BOOL bStr1 = ( eType1 != CELLTYPE_VALUE ); + if ( eType1 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell1)->IsValue() ) + bStr1 = FALSE; + BOOL bStr2 = ( eType2 != CELLTYPE_VALUE ); + if ( eType2 == CELLTYPE_FORMULA && ((ScFormulaCell*)pCell2)->IsValue() ) + bStr2 = FALSE; + + if ( bStr1 && bStr2 ) // nur Strings untereinander als String vergleichen! + { + String aStr1; + String aStr2; + if (eType1 == CELLTYPE_STRING) + ((ScStringCell*)pCell1)->GetString(aStr1); + else + GetString(nCell1Col, nCell1Row, aStr1); + if (eType2 == CELLTYPE_STRING) + ((ScStringCell*)pCell2)->GetString(aStr2); + else + GetString(nCell2Col, nCell2Row, aStr2); + BOOL bUserDef = aSortParam.bUserDef; + if (bUserDef) + { + ScUserListData* pData = + (ScUserListData*)(ScGlobal::GetUserList()->At( + aSortParam.nUserIndex)); + if (pData) + { + if ( aSortParam.bCaseSens ) + nRes = sal::static_int_cast<short>( pData->Compare(aStr1, aStr2) ); + else + nRes = sal::static_int_cast<short>( pData->ICompare(aStr1, aStr2) ); + } + else + bUserDef = FALSE; + + } + if (!bUserDef) + nRes = (short) pSortCollator->compareString( aStr1, aStr2 ); + } + else if ( bStr1 ) // String <-> Zahl + nRes = 1; // Zahl vorne + else if ( bStr2 ) // Zahl <-> String + nRes = -1; // Zahl vorne + else // Zahlen untereinander + { + double nVal1; + double nVal2; + if (eType1 == CELLTYPE_VALUE) + nVal1 = ((ScValueCell*)pCell1)->GetValue(); + else if (eType1 == CELLTYPE_FORMULA) + nVal1 = ((ScFormulaCell*)pCell1)->GetValue(); + else + nVal1 = 0; + if (eType2 == CELLTYPE_VALUE) + nVal2 = ((ScValueCell*)pCell2)->GetValue(); + else if (eType2 == CELLTYPE_FORMULA) + nVal2 = ((ScFormulaCell*)pCell2)->GetValue(); + else + nVal2 = 0; + if (nVal1 < nVal2) + nRes = -1; + else if (nVal1 > nVal2) + nRes = 1; + } + if ( !aSortParam.bAscending[nSort] ) + nRes = -nRes; + } + else + nRes = -1; + } + else + { + if ( pCell2 ) + nRes = 1; + else + nRes = 0; // beide leer + } + return nRes; +} + +short ScTable::Compare( ScSortInfoArray* pArray, SCCOLROW nIndex1, SCCOLROW nIndex2 ) +{ + short nRes; + USHORT nSort = 0; + do + { + ScSortInfo* pInfo1 = pArray->Get( nSort, nIndex1 ); + ScSortInfo* pInfo2 = pArray->Get( nSort, nIndex2 ); + if ( aSortParam.bByRow ) + nRes = CompareCell( nSort, + pInfo1->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo1->nOrg, + pInfo2->pCell, static_cast<SCCOL>(aSortParam.nField[nSort]), pInfo2->nOrg ); + else + nRes = CompareCell( nSort, + pInfo1->pCell, static_cast<SCCOL>(pInfo1->nOrg), aSortParam.nField[nSort], + pInfo2->pCell, static_cast<SCCOL>(pInfo2->nOrg), aSortParam.nField[nSort] ); + } while ( nRes == 0 && ++nSort < pArray->GetUsedSorts() ); + if( nRes == 0 ) + { + ScSortInfo* pInfo1 = pArray->Get( 0, nIndex1 ); + ScSortInfo* pInfo2 = pArray->Get( 0, nIndex2 ); + if( pInfo1->nOrg < pInfo2->nOrg ) + nRes = -1; + else if( pInfo1->nOrg > pInfo2->nOrg ) + nRes = 1; + } + return nRes; +} + +void ScTable::QuickSort( ScSortInfoArray* pArray, SCsCOLROW nLo, SCsCOLROW nHi ) +{ + if ((nHi - nLo) == 1) + { + if (Compare(pArray, nLo, nHi) > 0) + pArray->Swap( nLo, nHi ); + } + else + { + SCsCOLROW ni = nLo; + SCsCOLROW nj = nHi; + do + { + while ((ni <= nHi) && (Compare(pArray, ni, nLo)) < 0) + ni++; + while ((nj >= nLo) && (Compare(pArray, nLo, nj)) < 0) + nj--; + if (ni <= nj) + { + if (ni != nj) + pArray->Swap( ni, nj ); + ni++; + nj--; + } + } while (ni < nj); + if ((nj - nLo) < (nHi - ni)) + { + if (nLo < nj) + QuickSort(pArray, nLo, nj); + if (ni < nHi) + QuickSort(pArray, ni, nHi); + } + else + { + if (ni < nHi) + QuickSort(pArray, ni, nHi); + if (nLo < nj) + QuickSort(pArray, nLo, nj); + } + } +} + +void ScTable::SwapCol(SCCOL nCol1, SCCOL nCol2) +{ + for (SCROW nRow = aSortParam.nRow1; nRow <= aSortParam.nRow2; nRow++) + { + aCol[nCol1].SwapCell(nRow, aCol[nCol2]); + if (aSortParam.bIncludePattern) + { + const ScPatternAttr* pPat1 = GetPattern(nCol1, nRow); + const ScPatternAttr* pPat2 = GetPattern(nCol2, nRow); + if (pPat1 != pPat2) + { + SetPattern(nCol1, nRow, *pPat2, TRUE); + SetPattern(nCol2, nRow, *pPat1, TRUE); + } + } + } +} + +void ScTable::SwapRow(SCROW nRow1, SCROW nRow2) +{ + for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++) + { + aCol[nCol].SwapRow(nRow1, nRow2); + if (aSortParam.bIncludePattern) + { + const ScPatternAttr* pPat1 = GetPattern(nCol, nRow1); + const ScPatternAttr* pPat2 = GetPattern(nCol, nRow2); + if (pPat1 != pPat2) + { + SetPattern(nCol, nRow1, *pPat2, TRUE); + SetPattern(nCol, nRow2, *pPat1, TRUE); + } + } + } + if (bGlobalKeepQuery && pRowFlags) + { + BYTE nRow1Flags = pRowFlags->GetValue(nRow1); + BYTE nRow2Flags = pRowFlags->GetValue(nRow2); + BYTE nFlags1 = nRow1Flags & ( CR_HIDDEN | CR_FILTERED ); + BYTE nFlags2 = nRow2Flags & ( CR_HIDDEN | CR_FILTERED ); + pRowFlags->SetValue( nRow1, (nRow1Flags & ~( CR_HIDDEN | CR_FILTERED )) | nFlags2); + pRowFlags->SetValue( nRow2, (nRow2Flags & ~( CR_HIDDEN | CR_FILTERED )) | nFlags1); + } +} + +short ScTable::Compare(SCCOLROW nIndex1, SCCOLROW nIndex2) +{ + short nRes; + USHORT nSort = 0; + if (aSortParam.bByRow) + { + do + { + SCCOL nCol = static_cast<SCCOL>(aSortParam.nField[nSort]); + ScBaseCell* pCell1 = aCol[nCol].GetCell( nIndex1 ); + ScBaseCell* pCell2 = aCol[nCol].GetCell( nIndex2 ); + nRes = CompareCell( nSort, pCell1, nCol, nIndex1, pCell2, nCol, nIndex2 ); + } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] ); + } + else + { + do + { + SCROW nRow = aSortParam.nField[nSort]; + ScBaseCell* pCell1 = aCol[nIndex1].GetCell( nRow ); + ScBaseCell* pCell2 = aCol[nIndex2].GetCell( nRow ); + nRes = CompareCell( nSort, pCell1, static_cast<SCCOL>(nIndex1), + nRow, pCell2, static_cast<SCCOL>(nIndex2), nRow ); + } while ( nRes == 0 && ++nSort < nMaxSorts && aSortParam.bDoSort[nSort] ); + } + return nRes; +} + +BOOL ScTable::IsSorted( SCCOLROW nStart, SCCOLROW nEnd ) // ueber aSortParam +{ + for (SCCOLROW i=nStart; i<nEnd; i++) + { + if (Compare( i, i+1 ) > 0) + return FALSE; + } + return TRUE; +} + +void ScTable::DecoladeRow( ScSortInfoArray* pArray, SCROW nRow1, SCROW nRow2 ) +{ + SCROW nRow; + SCROW nMax = nRow2 - nRow1; + for (SCROW i = nRow1; (i + 4) <= nRow2; i += 4) + { + nRow = rand() % nMax; + pArray->Swap(i, nRow1 + nRow); + } +} + +void ScTable::Sort(const ScSortParam& rSortParam, BOOL bKeepQuery) +{ + aSortParam = rSortParam; + InitSortCollator( rSortParam ); + bGlobalKeepQuery = bKeepQuery; + if (rSortParam.bByRow) + { + SCROW nLastRow = 0; + for (SCCOL nCol = aSortParam.nCol1; nCol <= aSortParam.nCol2; nCol++) + nLastRow = Max(nLastRow, aCol[nCol].GetLastDataPos()); + nLastRow = Min(nLastRow, aSortParam.nRow2); + SCROW nRow1 = (rSortParam.bHasHeader ? + aSortParam.nRow1 + 1 : aSortParam.nRow1); + if (!IsSorted(nRow1, nLastRow)) + { + ScProgress aProgress( pDocument->GetDocumentShell(), + ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastRow - nRow1 ); + ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, nLastRow ); + if ( nLastRow - nRow1 > 255 ) + DecoladeRow( pArray, nRow1, nLastRow ); + QuickSort( pArray, nRow1, nLastRow ); + SortReorder( pArray, aProgress ); + delete pArray; + // #158377# #i59745# update position of caption objects of cell notes + ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( aSortParam.nCol1, nRow1, nTab, aSortParam.nCol2, nLastRow, nTab ) ); + } + } + else + { + SCCOL nLastCol; + for (nLastCol = aSortParam.nCol2; + (nLastCol > aSortParam.nCol1) && aCol[nLastCol].IsEmptyBlock(aSortParam.nRow1, aSortParam.nRow2); nLastCol--) + { + } + SCCOL nCol1 = (rSortParam.bHasHeader ? + aSortParam.nCol1 + 1 : aSortParam.nCol1); + if (!IsSorted(nCol1, nLastCol)) + { + ScProgress aProgress( pDocument->GetDocumentShell(), + ScGlobal::GetRscString(STR_PROGRESS_SORTING), nLastCol - nCol1 ); + ScSortInfoArray* pArray = CreateSortInfoArray( nCol1, nLastCol ); + QuickSort( pArray, nCol1, nLastCol ); + SortReorder( pArray, aProgress ); + delete pArray; + // #158377# #i59745# update position of caption objects of cell notes + ScNoteUtil::UpdateCaptionPositions( *pDocument, ScRange( nCol1, aSortParam.nRow1, nTab, nLastCol, aSortParam.nRow2, nTab ) ); + } + } + DestroySortCollator(); +} + + +// Testen, ob beim Loeschen von Zwischenergebnissen andere Daten mit geloescht werden +// (fuer Hinweis-Box) + +BOOL ScTable::TestRemoveSubTotals( const ScSubTotalParam& rParam ) +{ + SCCOL nStartCol = rParam.nCol1; + SCROW nStartRow = rParam.nRow1 + 1; // Header + SCCOL nEndCol = rParam.nCol2; + SCROW nEndRow = rParam.nRow2; + + SCCOL nCol; + SCROW nRow; + ScBaseCell* pCell; + + BOOL bWillDelete = FALSE; + for ( nCol=nStartCol; nCol<=nEndCol && !bWillDelete; nCol++ ) + { + ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow ); + while ( aIter.Next( nRow, pCell ) && !bWillDelete ) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pCell)->IsSubTotal()) + { + for (SCCOL nTestCol=0; nTestCol<=MAXCOL; nTestCol++) + if (nTestCol<nStartCol || nTestCol>nEndCol) + if (aCol[nTestCol].HasDataAt(nRow)) + bWillDelete = TRUE; + } + } + } + return bWillDelete; +} + +// alte Ergebnisse loeschen +// rParam.nRow2 wird veraendert ! + +void ScTable::RemoveSubTotals( ScSubTotalParam& rParam ) +{ + SCCOL nStartCol = rParam.nCol1; + SCROW nStartRow = rParam.nRow1 + 1; // Header + SCCOL nEndCol = rParam.nCol2; + SCROW nEndRow = rParam.nRow2; // wird veraendert + + SCCOL nCol; + SCROW nRow; + ScBaseCell* pCell; + + for ( nCol=nStartCol; nCol<=nEndCol; nCol++ ) + { + ScColumnIterator aIter( &aCol[nCol],nStartRow,nEndRow ); + while ( aIter.Next( nRow, pCell ) ) + { + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pCell)->IsSubTotal()) + { + SetRowFlags(nRow+1,GetRowFlags(nRow+1)&(~CR_MANUALBREAK)); + pDocument->DeleteRow( 0,nTab, MAXCOL,nTab, nRow, 1 ); + --nEndRow; + aIter = ScColumnIterator( &aCol[nCol],nRow,nEndRow ); + } + } + } + + rParam.nRow2 = nEndRow; // neues Ende +} + +// harte Zahlenformate loeschen (fuer Ergebnisformeln) + +void lcl_RemoveNumberFormat( ScTable* pTab, SCCOL nCol, SCROW nRow ) +{ + const ScPatternAttr* pPattern = pTab->GetPattern( nCol, nRow ); + if ( pPattern->GetItemSet().GetItemState( ATTR_VALUE_FORMAT, FALSE ) + == SFX_ITEM_SET ) + { + ScPatternAttr aNewPattern( *pPattern ); + SfxItemSet& rSet = aNewPattern.GetItemSet(); + rSet.ClearItem( ATTR_VALUE_FORMAT ); + rSet.ClearItem( ATTR_LANGUAGE_FORMAT ); + pTab->SetPattern( nCol, nRow, aNewPattern, TRUE ); + } +} + + +// at least MSC needs this at linkage level to be able to use it in a template +typedef struct lcl_ScTable_DoSubTotals_RowEntry +{ + USHORT nGroupNo; + SCROW nSubStartRow; + SCROW nDestRow; + SCROW nFuncStart; + SCROW nFuncEnd; +} RowEntry; + +// neue Zwischenergebnisse +// rParam.nRow2 wird veraendert ! + +BOOL ScTable::DoSubTotals( ScSubTotalParam& rParam ) +{ + SCCOL nStartCol = rParam.nCol1; + SCROW nStartRow = rParam.nRow1 + 1; // Header + SCCOL nEndCol = rParam.nCol2; + SCROW nEndRow = rParam.nRow2; // wird veraendert + USHORT i; + + // Leerzeilen am Ende weglassen, + // damit alle Ueberlaeufe (MAXROW) bei InsertRow gefunden werden (#35180#) + // Wenn sortiert wurde, sind alle Leerzeilen am Ende. + SCSIZE nEmpty = GetEmptyLinesInBlock( nStartCol, nStartRow, nEndCol, nEndRow, DIR_BOTTOM ); + nEndRow -= nEmpty; + + USHORT nLevelCount = 0; // Anzahl Gruppierungen + BOOL bDoThis = TRUE; + for (i=0; i<MAXSUBTOTAL && bDoThis; i++) + if (rParam.bGroupActive[i]) + nLevelCount = i+1; + else + bDoThis = FALSE; + + if (nLevelCount==0) // nichts tun + return TRUE; + + SCCOL* nGroupCol = rParam.nField; // Spalten nach denen + // gruppiert wird + + // #44444# Durch (leer) als eigene Kategorie muss immer auf + // Teilergebniszeilen aus den anderen Spalten getestet werden + // (frueher nur, wenn eine Spalte mehrfach vorkam) + BOOL bTestPrevSub = ( nLevelCount > 1 ); + + String aSubString; + String aOutString; + + BOOL bIgnoreCase = !rParam.bCaseSens; + + String *pCompString[MAXSUBTOTAL]; // Pointer wegen Compiler-Problemen + for (i=0; i<MAXSUBTOTAL; i++) + pCompString[i] = new String; + + //! sortieren? + + ScStyleSheet* pStyle = (ScStyleSheet*) pDocument->GetStyleSheetPool()->Find( + ScGlobal::GetRscString(STR_STYLENAME_RESULT), SFX_STYLE_FAMILY_PARA ); + + BOOL bSpaceLeft = TRUE; // Erfolg beim Einfuegen? + + // #90279# For performance reasons collect formula entries so their + // references don't have to be tested for updates each time a new row is + // inserted + RowEntry aRowEntry; + ::std::vector< RowEntry > aRowVector; + + for (USHORT nLevel=0; nLevel<=nLevelCount && bSpaceLeft; nLevel++) // incl. Gesamtergebnis + { + BOOL bTotal = ( nLevel == nLevelCount ); + aRowEntry.nGroupNo = bTotal ? 0 : (nLevelCount-nLevel-1); + + // how many results per level + SCCOL nResCount = rParam.nSubTotals[aRowEntry.nGroupNo]; + // result functions + ScSubTotalFunc* eResFunc = rParam.pFunctions[aRowEntry.nGroupNo]; + + if (nResCount > 0) // sonst nur sortieren + { + for (i=0; i<=aRowEntry.nGroupNo; i++) + { + GetString( nGroupCol[i], nStartRow, aSubString ); + if ( bIgnoreCase ) + *pCompString[i] = ScGlobal::pCharClass->upper( aSubString ); + else + *pCompString[i] = aSubString; + } // aSubString bleibt auf dem letzten stehen + + BOOL bBlockVis = FALSE; // Gruppe eingeblendet? + aRowEntry.nSubStartRow = nStartRow; + for (SCROW nRow=nStartRow; nRow<=nEndRow+1 && bSpaceLeft; nRow++) + { + BOOL bChanged; + if (nRow>nEndRow) + bChanged = TRUE; + else + { + bChanged = FALSE; + if (!bTotal) + { + String aString; + for (i=0; i<=aRowEntry.nGroupNo && !bChanged; i++) + { + GetString( nGroupCol[i], nRow, aString ); + if (bIgnoreCase) + ScGlobal::pCharClass->toUpper( aString ); + // #41427# wenn sortiert, ist "leer" eine eigene Gruppe + // sonst sind leere Zellen unten erlaubt + bChanged = ( ( aString.Len() || rParam.bDoSort ) && + aString != *pCompString[i] ); + } + if ( bChanged && bTestPrevSub ) + { + // No group change on rows that will contain subtotal formulas + for ( ::std::vector< RowEntry >::const_iterator + iEntry( aRowVector.begin()); + iEntry != aRowVector.end(); ++iEntry) + { + if ( iEntry->nDestRow == nRow ) + { + bChanged = FALSE; + break; + } + } + } + } + } + if ( bChanged ) + { + aRowEntry.nDestRow = nRow; + aRowEntry.nFuncStart = aRowEntry.nSubStartRow; + aRowEntry.nFuncEnd = nRow-1; + + bSpaceLeft = pDocument->InsertRow( 0, nTab, MAXCOL, nTab, + aRowEntry.nDestRow, 1 ); + DBShowRow( aRowEntry.nDestRow, bBlockVis ); + bBlockVis = FALSE; + if ( rParam.bPagebreak && nRow < MAXROW && + aRowEntry.nSubStartRow != nStartRow && nLevel == 0) + SetRowFlags( aRowEntry.nSubStartRow, + GetRowFlags(aRowEntry.nSubStartRow) | + CR_MANUALBREAK); + + if (bSpaceLeft) + { + for ( ::std::vector< RowEntry >::iterator iMove( + aRowVector.begin() ); + iMove != aRowVector.end(); ++iMove) + { + if ( aRowEntry.nDestRow <= iMove->nSubStartRow ) + ++iMove->nSubStartRow; + if ( aRowEntry.nDestRow <= iMove->nDestRow ) + ++iMove->nDestRow; + if ( aRowEntry.nDestRow <= iMove->nFuncStart ) + ++iMove->nFuncStart; + if ( aRowEntry.nDestRow <= iMove->nFuncEnd ) + ++iMove->nFuncEnd; + } + // collect formula positions + aRowVector.push_back( aRowEntry ); + + if (bTotal) // "Gesamtergebnis" + aOutString = ScGlobal::GetRscString( STR_TABLE_GESAMTERGEBNIS ); + else + { // " Ergebnis" + aOutString = aSubString; + if (!aOutString.Len()) + aOutString = ScGlobal::GetRscString( STR_EMPTYDATA ); + aOutString += ' '; + USHORT nStrId = STR_TABLE_ERGEBNIS; + if ( nResCount == 1 ) + switch ( eResFunc[0] ) + { + case SUBTOTAL_FUNC_AVE: nStrId = STR_FUN_TEXT_AVG; break; + case SUBTOTAL_FUNC_CNT: + case SUBTOTAL_FUNC_CNT2: nStrId = STR_FUN_TEXT_COUNT; break; + case SUBTOTAL_FUNC_MAX: nStrId = STR_FUN_TEXT_MAX; break; + case SUBTOTAL_FUNC_MIN: nStrId = STR_FUN_TEXT_MIN; break; + case SUBTOTAL_FUNC_PROD: nStrId = STR_FUN_TEXT_PRODUCT; break; + case SUBTOTAL_FUNC_STD: + case SUBTOTAL_FUNC_STDP: nStrId = STR_FUN_TEXT_STDDEV; break; + case SUBTOTAL_FUNC_SUM: nStrId = STR_FUN_TEXT_SUM; break; + case SUBTOTAL_FUNC_VAR: + case SUBTOTAL_FUNC_VARP: nStrId = STR_FUN_TEXT_VAR; break; + default: + { + // added to avoid warnings + } + } + aOutString += ScGlobal::GetRscString( nStrId ); + } + SetString( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, nTab, aOutString ); + ApplyStyle( nGroupCol[aRowEntry.nGroupNo], aRowEntry.nDestRow, *pStyle ); + +/* if (rParam.bPagebreak && nRow < MAXROW) + { + BYTE nFlags = GetRowFlags( nRow+1 ); + nFlags |= CR_MANUALBREAK; + SetRowFlags( nRow+1, nFlags ); + } +*/ + ++nRow; + ++nEndRow; + aRowEntry.nSubStartRow = nRow; + for (i=0; i<=aRowEntry.nGroupNo; i++) + { + GetString( nGroupCol[i], nRow, aSubString ); + if ( bIgnoreCase ) + *pCompString[i] = ScGlobal::pCharClass->upper( aSubString ); + else + *pCompString[i] = aSubString; + } + } + } + if (!pRowFlags) + bBlockVis = TRUE; + else + if ( (pRowFlags->GetValue(nRow) & CR_FILTERED) == 0 ) + bBlockVis = TRUE; + } + } + else + { +// DBG_ERROR( "nSubTotals==0 bei DoSubTotals" ); + } + } + + // now insert the formulas + ScComplexRefData aRef; + aRef.InitFlags(); + aRef.Ref1.nTab = nTab; + aRef.Ref2.nTab = nTab; + for ( ::std::vector< RowEntry >::const_iterator iEntry( aRowVector.begin()); + iEntry != aRowVector.end(); ++iEntry) + { + SCCOL nResCount = rParam.nSubTotals[iEntry->nGroupNo]; + SCCOL* nResCols = rParam.pSubTotals[iEntry->nGroupNo]; + ScSubTotalFunc* eResFunc = rParam.pFunctions[iEntry->nGroupNo]; + for ( SCCOL nResult=0; nResult < nResCount; ++nResult ) + { + aRef.Ref1.nCol = nResCols[nResult]; + aRef.Ref1.nRow = iEntry->nFuncStart; + aRef.Ref2.nCol = nResCols[nResult]; + aRef.Ref2.nRow = iEntry->nFuncEnd; + + ScTokenArray aArr; + aArr.AddOpCode( ocSubTotal ); + aArr.AddOpCode( ocOpen ); + aArr.AddDouble( (double) eResFunc[nResult] ); + aArr.AddOpCode( ocSep ); + aArr.AddDoubleReference( aRef ); + aArr.AddOpCode( ocClose ); + aArr.AddOpCode( ocStop ); + ScBaseCell* pCell = new ScFormulaCell( pDocument, ScAddress( + nResCols[nResult], iEntry->nDestRow, nTab), &aArr ); + PutCell( nResCols[nResult], iEntry->nDestRow, pCell ); + + if ( nResCols[nResult] != nGroupCol[iEntry->nGroupNo] ) + { + ApplyStyle( nResCols[nResult], iEntry->nDestRow, *pStyle ); + + // Zahlformat loeschen + lcl_RemoveNumberFormat( this, nResCols[nResult], iEntry->nDestRow ); + } + } + + } + + //! je nach Einstellung Zwischensummen-Zeilen nach oben verschieben ? + + //! Outlines direkt erzeugen? + + if (bSpaceLeft) + DoAutoOutline( nStartCol, nStartRow, nEndCol, nEndRow ); + + for (i=0; i<MAXSUBTOTAL; i++) + delete pCompString[i]; + + rParam.nRow2 = nEndRow; // neues Ende + return bSpaceLeft; +} + + +BOOL ScTable::ValidQuery(SCROW nRow, const ScQueryParam& rParam, + BOOL* pSpecial /* =NULL */ , ScBaseCell* pCell /* =NULL */ , + BOOL* pbTestEqualCondition /* = NULL */ ) +{ + if (!rParam.GetEntry(0).bDoQuery) + return TRUE; + + //--------------------------------------------------------------- + + const SCSIZE nFixedBools = 32; + BOOL aBool[nFixedBools]; + BOOL aTest[nFixedBools]; + SCSIZE nEntryCount = rParam.GetEntryCount(); + BOOL* pPasst = ( nEntryCount <= nFixedBools ? &aBool[0] : new BOOL[nEntryCount] ); + BOOL* pTest = ( nEntryCount <= nFixedBools ? &aTest[0] : new BOOL[nEntryCount] ); + + long nPos = -1; + SCSIZE i = 0; + BOOL bMatchWholeCell = pDocument->GetDocOptions().IsMatchWholeCell(); + CollatorWrapper* pCollator = (rParam.bCaseSens ? ScGlobal::GetCaseCollator() : + ScGlobal::GetCollator()); + ::utl::TransliterationWrapper* pTransliteration = (rParam.bCaseSens ? + ScGlobal::GetCaseTransliteration() : ScGlobal::GetpTransliteration()); + + while ( (i < nEntryCount) && rParam.GetEntry(i).bDoQuery ) + { + ScQueryEntry& rEntry = rParam.GetEntry(i); + // we can only handle one single direct query + if ( !pCell || i > 0 ) + pCell = GetCell( static_cast<SCCOL>(rEntry.nField), nRow ); + + BOOL bOk = FALSE; + BOOL bTestEqual = FALSE; + + if ( pSpecial && pSpecial[i] ) + { + if (rEntry.nVal == SC_EMPTYFIELDS) + bOk = !( aCol[rEntry.nField].HasDataAt( nRow ) ); + else // if (rEntry.nVal == SC_NONEMPTYFIELDS) + bOk = aCol[rEntry.nField].HasDataAt( nRow ); + } + else if ( !rEntry.bQueryByString && (pCell ? pCell->HasValueData() : + HasValueData( static_cast<SCCOL>(rEntry.nField), nRow))) + { // by Value + double nCellVal; + if ( pCell ) + { + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + nCellVal = ((ScValueCell*)pCell)->GetValue(); + break; + case CELLTYPE_FORMULA : + nCellVal = ((ScFormulaCell*)pCell)->GetValue(); + break; + default: + nCellVal = 0.0; + } + + } + else + nCellVal = GetValue( static_cast<SCCOL>(rEntry.nField), nRow ); + switch (rEntry.eOp) + { + case SC_EQUAL : + bOk = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + case SC_LESS : + bOk = (nCellVal < rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + case SC_GREATER : + bOk = (nCellVal > rEntry.nVal) && !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + case SC_LESS_EQUAL : + bOk = (nCellVal < rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + if ( bOk && pbTestEqualCondition ) + bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + case SC_GREATER_EQUAL : + bOk = (nCellVal > rEntry.nVal) || ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + if ( bOk && pbTestEqualCondition ) + bTestEqual = ::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + case SC_NOT_EQUAL : + bOk = !::rtl::math::approxEqual( nCellVal, rEntry.nVal ); + break; + default: + { + // added to avoid warnings + } + } + } + else if ( (rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL) || + (rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN || + rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH || + rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH) || + (rEntry.bQueryByString && (pCell ? pCell->HasStringData() : + HasStringData( + static_cast<SCCOL>(rEntry.nField), + nRow)))) + { // by String + String aCellStr; + if( rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN + || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH + || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) + bMatchWholeCell = FALSE; + if ( pCell ) + { + if (pCell->GetCellType() != CELLTYPE_NOTE) + { + ULONG nFormat = GetNumberFormat( static_cast<SCCOL>(rEntry.nField), nRow ); + ScCellFormat::GetInputString( pCell, nFormat, aCellStr, *(pDocument->GetFormatTable()) ); + } + } + else + GetInputString( static_cast<SCCOL>(rEntry.nField), nRow, aCellStr ); + + BOOL bRealRegExp = (rParam.bRegExp && ((rEntry.eOp == SC_EQUAL) + || (rEntry.eOp == SC_NOT_EQUAL) || (rEntry.eOp == SC_CONTAINS) + || (rEntry.eOp == SC_DOES_NOT_CONTAIN) || (rEntry.eOp == SC_BEGINS_WITH) + || (rEntry.eOp == SC_ENDS_WITH) || (rEntry.eOp == SC_DOES_NOT_BEGIN_WITH) + || (rEntry.eOp == SC_DOES_NOT_END_WITH))); + BOOL bTestRegExp = (pbTestEqualCondition && rParam.bRegExp + && ((rEntry.eOp == SC_LESS_EQUAL) + || (rEntry.eOp == SC_GREATER_EQUAL))); + if ( bRealRegExp || bTestRegExp ) + { + xub_StrLen nStart = 0; + xub_StrLen nEnd = aCellStr.Len(); + + // from 614 on, nEnd is behind the found text + BOOL bMatch = FALSE; + if ( rEntry.eOp == SC_ENDS_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) + { + nEnd = 0; + nStart = aCellStr.Len(); + bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens ) + ->SearchBkwrd( aCellStr, &nStart, &nEnd ); + } + else + { + bMatch = (BOOL) rEntry.GetSearchTextPtr( rParam.bCaseSens ) + ->SearchFrwrd( aCellStr, &nStart, &nEnd ); + } + if ( bMatch && bMatchWholeCell + && (nStart != 0 || nEnd != aCellStr.Len()) ) + bMatch = FALSE; // RegExp must match entire cell string + if ( bRealRegExp ) + switch (rEntry.eOp) + { + case SC_EQUAL: + case SC_CONTAINS: + bOk = bMatch; + break; + case SC_NOT_EQUAL: + case SC_DOES_NOT_CONTAIN: + bOk = !bMatch; + break; + case SC_BEGINS_WITH: + bOk = ( bMatch && (nStart == 0) ); + break; + case SC_DOES_NOT_BEGIN_WITH: + bOk = !( bMatch && (nStart == 0) ); + break; + case SC_ENDS_WITH: + bOk = ( bMatch && (nEnd == aCellStr.Len()) ); + break; + case SC_DOES_NOT_END_WITH: + bOk = !( bMatch && (nEnd == aCellStr.Len()) ); + break; + default: + { + // added to avoid warnings + } + } + else + bTestEqual = bMatch; + } + if ( !bRealRegExp ) + { + if ( rEntry.eOp == SC_EQUAL || rEntry.eOp == SC_NOT_EQUAL + || rEntry.eOp == SC_CONTAINS || rEntry.eOp == SC_DOES_NOT_CONTAIN + || rEntry.eOp == SC_BEGINS_WITH || rEntry.eOp == SC_ENDS_WITH + || rEntry.eOp == SC_DOES_NOT_BEGIN_WITH || rEntry.eOp == SC_DOES_NOT_END_WITH ) + { + if ( !rEntry.bQueryByString && rEntry.pStr->Len() == 0 ) + { + // #i18374# When used from functions (match, countif, sumif, vlookup, hlookup, lookup), + // the query value is assigned directly, and the string is empty. In that case, + // don't find any string (isEqual would find empty string results in formula cells). + bOk = FALSE; + if ( rEntry.eOp == SC_NOT_EQUAL ) + bOk = !bOk; + } + else if ( bMatchWholeCell ) + { + bOk = pTransliteration->isEqual( aCellStr, *rEntry.pStr ); + if ( rEntry.eOp == SC_NOT_EQUAL ) + bOk = !bOk; + } + else + { + String aCell( pTransliteration->transliterate( + aCellStr, ScGlobal::eLnge, 0, aCellStr.Len(), + NULL ) ); + String aQuer( pTransliteration->transliterate( + *rEntry.pStr, ScGlobal::eLnge, 0, rEntry.pStr->Len(), + NULL ) ); + xub_StrLen nIndex = (rEntry.eOp == SC_ENDS_WITH + || rEntry.eOp == SC_DOES_NOT_END_WITH)? (aCell.Len()-aQuer.Len()):0; + xub_StrLen nStrPos = aCell.Search( aQuer, nIndex ); + switch (rEntry.eOp) + { + case SC_EQUAL: + case SC_CONTAINS: + bOk = ( nStrPos != STRING_NOTFOUND ); + break; + case SC_NOT_EQUAL: + case SC_DOES_NOT_CONTAIN: + bOk = ( nStrPos == STRING_NOTFOUND ); + break; + case SC_BEGINS_WITH: + bOk = ( nStrPos == 0 ); + break; + case SC_DOES_NOT_BEGIN_WITH: + bOk = ( nStrPos != 0 ); + break; + case SC_ENDS_WITH: + bOk = ( nStrPos + aQuer.Len() == aCell.Len() ); + break; + case SC_DOES_NOT_END_WITH: + bOk = ( nStrPos + aQuer.Len() != aCell.Len() ); + break; + default: + { + // added to avoid warnings + } + } + } + } + else + { // use collator here because data was probably sorted + sal_Int32 nCompare = pCollator->compareString( + aCellStr, *rEntry.pStr ); + switch (rEntry.eOp) + { + case SC_LESS : + bOk = (nCompare < 0); + break; + case SC_GREATER : + bOk = (nCompare > 0); + break; + case SC_LESS_EQUAL : + bOk = (nCompare <= 0); + if ( bOk && pbTestEqualCondition && !bTestEqual ) + bTestEqual = (nCompare == 0); + break; + case SC_GREATER_EQUAL : + bOk = (nCompare >= 0); + if ( bOk && pbTestEqualCondition && !bTestEqual ) + bTestEqual = (nCompare == 0); + break; + default: + { + // added to avoid warnings + } + } + } + } + } + else if (rParam.bMixedComparison) + { + if (rEntry.bQueryByString && + (rEntry.eOp == SC_LESS || rEntry.eOp == SC_LESS_EQUAL) && + (pCell ? pCell->HasValueData() : + HasValueData( static_cast<SCCOL>(rEntry.nField), nRow))) + { + bOk = TRUE; + } + else if (!rEntry.bQueryByString && + (rEntry.eOp == SC_GREATER || rEntry.eOp == SC_GREATER_EQUAL) && + (pCell ? pCell->HasStringData() : + HasStringData( static_cast<SCCOL>(rEntry.nField), nRow))) + { + bOk = TRUE; + } + } + + if (nPos == -1) + { + nPos++; + pPasst[nPos] = bOk; + pTest[nPos] = bTestEqual; + } + else + { + if (rEntry.eConnect == SC_AND) + { + pPasst[nPos] = pPasst[nPos] && bOk; + pTest[nPos] = pTest[nPos] && bTestEqual; + } + else + { + nPos++; + pPasst[nPos] = bOk; + pTest[nPos] = bTestEqual; + } + } + i++; + } + + for ( long j=1; j <= nPos; j++ ) + { + pPasst[0] = pPasst[0] || pPasst[j]; + pTest[0] = pTest[0] || pTest[j]; + } + + BOOL bRet = pPasst[0]; + if ( pPasst != &aBool[0] ) + delete [] pPasst; + if ( pbTestEqualCondition ) + *pbTestEqualCondition = pTest[0]; + if ( pTest != &aTest[0] ) + delete [] pTest; + + return bRet; +} + +void ScTable::TopTenQuery( ScQueryParam& rParam ) +{ + BOOL bSortCollatorInitialized = FALSE; + SCSIZE nEntryCount = rParam.GetEntryCount(); + SCROW nRow1 = (rParam.bHasHeader ? rParam.nRow1 + 1 : rParam.nRow1); + SCSIZE nCount = static_cast<SCSIZE>(rParam.nRow2 - nRow1 + 1); + for ( SCSIZE i=0; (i<nEntryCount) && (rParam.GetEntry(i).bDoQuery); i++ ) + { + ScQueryEntry& rEntry = rParam.GetEntry(i); + switch ( rEntry.eOp ) + { + case SC_TOPVAL: + case SC_BOTVAL: + case SC_TOPPERC: + case SC_BOTPERC: + { + ScSortParam aLocalSortParam( rParam, static_cast<SCCOL>(rEntry.nField) ); + aSortParam = aLocalSortParam; // used in CreateSortInfoArray, Compare + if ( !bSortCollatorInitialized ) + { + bSortCollatorInitialized = TRUE; + InitSortCollator( aLocalSortParam ); + } + ScSortInfoArray* pArray = CreateSortInfoArray( nRow1, rParam.nRow2 ); + DecoladeRow( pArray, nRow1, rParam.nRow2 ); + QuickSort( pArray, nRow1, rParam.nRow2 ); + ScSortInfo** ppInfo = pArray->GetFirstArray(); + SCSIZE nValidCount = nCount; + // keine Note-/Leerzellen zaehlen, sind ans Ende sortiert + while ( nValidCount > 0 && ( ppInfo[nValidCount-1]->pCell == NULL || + ppInfo[nValidCount-1]->pCell->GetCellType() == CELLTYPE_NOTE ) ) + nValidCount--; + // keine Strings zaehlen, sind zwischen Value und Leer + while ( nValidCount > 0 + && ppInfo[nValidCount-1]->pCell->HasStringData() ) + nValidCount--; + if ( nValidCount > 0 ) + { + if ( rEntry.bQueryByString ) + { // dat wird nix + rEntry.bQueryByString = FALSE; + rEntry.nVal = 10; // 10 bzw. 10% + } + SCSIZE nVal = (rEntry.nVal >= 1 ? static_cast<SCSIZE>(rEntry.nVal) : 1); + SCSIZE nOffset = 0; + switch ( rEntry.eOp ) + { + case SC_TOPVAL: + { + rEntry.eOp = SC_GREATER_EQUAL; + if ( nVal > nValidCount ) + nVal = nValidCount; + nOffset = nValidCount - nVal; // 1 <= nVal <= nValidCount + } + break; + case SC_BOTVAL: + { + rEntry.eOp = SC_LESS_EQUAL; + if ( nVal > nValidCount ) + nVal = nValidCount; + nOffset = nVal - 1; // 1 <= nVal <= nValidCount + } + break; + case SC_TOPPERC: + { + rEntry.eOp = SC_GREATER_EQUAL; + if ( nVal > 100 ) + nVal = 100; + nOffset = nValidCount - (nValidCount * nVal / 100); + if ( nOffset >= nValidCount ) + nOffset = nValidCount - 1; + } + break; + case SC_BOTPERC: + { + rEntry.eOp = SC_LESS_EQUAL; + if ( nVal > 100 ) + nVal = 100; + nOffset = (nValidCount * nVal / 100); + if ( nOffset >= nValidCount ) + nOffset = nValidCount - 1; + } + break; + default: + { + // added to avoid warnings + } + } + ScBaseCell* pCell = ppInfo[nOffset]->pCell; + if ( pCell->HasValueData() ) + { + if ( pCell->GetCellType() == CELLTYPE_VALUE ) + rEntry.nVal = ((ScValueCell*)pCell)->GetValue(); + else + rEntry.nVal = ((ScFormulaCell*)pCell)->GetValue(); + } + else + { + DBG_ERRORFILE( "TopTenQuery: pCell kein ValueData" ); + rEntry.eOp = SC_GREATER_EQUAL; + rEntry.nVal = 0; + } + } + else + { + rEntry.eOp = SC_GREATER_EQUAL; + rEntry.bQueryByString = FALSE; + rEntry.nVal = 0; + } + delete pArray; + } + break; + default: + { + // added to avoid warnings + } + } + } + if ( bSortCollatorInitialized ) + DestroySortCollator(); +} + +static void lcl_PrepareQuery( ScDocument* pDoc, ScTable* pTab, ScQueryParam& rParam, BOOL* pSpecial ) +{ + bool bTopTen = false; + SCSIZE nEntryCount = rParam.GetEntryCount(); + + for ( SCSIZE i = 0; i < nEntryCount; ++i ) + { + pSpecial[i] = FALSE; + ScQueryEntry& rEntry = rParam.GetEntry(i); + if ( rEntry.bDoQuery ) + { + if ( rEntry.bQueryByString ) + { + sal_uInt32 nIndex = 0; + rEntry.bQueryByString = !( pDoc->GetFormatTable()-> + IsNumberFormat( *rEntry.pStr, nIndex, rEntry.nVal ) ); + } + else + { + // #58736# call from UNO or second call from autofilter + if ( rEntry.nVal == SC_EMPTYFIELDS || rEntry.nVal == SC_NONEMPTYFIELDS ) + { + pSpecial[i] = TRUE; + } + } + if ( !bTopTen ) + { + switch ( rEntry.eOp ) + { + case SC_TOPVAL: + case SC_BOTVAL: + case SC_TOPPERC: + case SC_BOTPERC: + { + bTopTen = true; + } + break; + default: + { + } + } + } + } + } + + if ( bTopTen ) + { + pTab->TopTenQuery( rParam ); + } +} + +SCSIZE ScTable::Query(ScQueryParam& rParamOrg, BOOL bKeepSub) +{ + ScQueryParam aParam( rParamOrg ); + ScStrCollection aScStrCollection; + StrData* pStrData = NULL; + + BOOL bStarted = FALSE; + BOOL bOldResult = TRUE; + SCROW nOldStart = 0; + SCROW nOldEnd = 0; + + SCSIZE nCount = 0; + SCROW nOutRow = 0; + SCROW nHeader = aParam.bHasHeader ? 1 : 0; + + SCSIZE nEntryCount = aParam.GetEntryCount(); + BOOL* pSpecial = new BOOL[nEntryCount]; + lcl_PrepareQuery( pDocument, this, aParam, pSpecial ); + + if (!aParam.bInplace) + { + nOutRow = aParam.nDestRow + nHeader; + if (nHeader > 0) + CopyData( aParam.nCol1, aParam.nRow1, aParam.nCol2, aParam.nRow1, + aParam.nDestCol, aParam.nDestRow, aParam.nDestTab ); + } + + for (SCROW j=aParam.nRow1 + nHeader; j<=aParam.nRow2; j++) + { + BOOL bResult; // Filterergebnis + BOOL bValid = ValidQuery(j, aParam, pSpecial); + if (!bValid && bKeepSub) // Subtotals stehenlassen + { + for (SCCOL nCol=aParam.nCol1; nCol<=aParam.nCol2 && !bValid; nCol++) + { + ScBaseCell* pCell; + pCell = GetCell( nCol, j ); + if ( pCell ) + if ( pCell->GetCellType() == CELLTYPE_FORMULA ) + if (((ScFormulaCell*)pCell)->IsSubTotal()) + if (RefVisible((ScFormulaCell*)pCell)) + bValid = TRUE; + } + } + if (bValid) + { + if (aParam.bDuplicate) + bResult = TRUE; + else + { + String aStr; + for (SCCOL k=aParam.nCol1; k <= aParam.nCol2; k++) + { + String aCellStr; + GetString(k, j, aCellStr); + aStr += aCellStr; + aStr += (sal_Unicode)1; + } + pStrData = new StrData(aStr); + + BOOL bIsUnique = TRUE; + if (pStrData) + bIsUnique = aScStrCollection.Insert(pStrData); + if (bIsUnique) + bResult = TRUE; + else + { + delete pStrData; + bResult = FALSE; + } + } + } + else + bResult = FALSE; + + if (aParam.bInplace) + { + if (bResult == bOldResult && bStarted) + nOldEnd = j; + else + { + if (bStarted) + DBShowRows(nOldStart,nOldEnd, bOldResult); + nOldStart = nOldEnd = j; + bOldResult = bResult; + } + bStarted = TRUE; + } + else + { + if (bResult) + { + CopyData( aParam.nCol1,j, aParam.nCol2,j, aParam.nDestCol,nOutRow,aParam.nDestTab ); + ++nOutRow; + } + } + if (bResult) + ++nCount; + } + + if (aParam.bInplace && bStarted) + DBShowRows(nOldStart,nOldEnd, bOldResult); + + delete[] pSpecial; + + return nCount; +} + +BOOL ScTable::CreateExcelQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) +{ + BOOL bValid = TRUE; + SCCOL* pFields = new SCCOL[nCol2-nCol1+1]; + String aCellStr; + SCCOL nCol = nCol1; + DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); + SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); + SCROW nDBRow1 = rQueryParam.nRow1; + SCCOL nDBCol2 = rQueryParam.nCol2; + // Erste Zeile muessen Spaltenkoepfe sein + while (bValid && (nCol <= nCol2)) + { + String aQueryStr; + GetUpperCellString(nCol, nRow1, aQueryStr); + BOOL bFound = FALSE; + SCCOL i = rQueryParam.nCol1; + while (!bFound && (i <= nDBCol2)) + { + if ( nTab == nDBTab ) + GetUpperCellString(i, nDBRow1, aCellStr); + else + pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aCellStr); + bFound = (aCellStr == aQueryStr); + if (!bFound) i++; + } + if (bFound) + pFields[nCol - nCol1] = i; + else + bValid = FALSE; + nCol++; + } + if (bValid) + { + ULONG nVisible = 0; + for ( nCol=nCol1; nCol<=nCol2; nCol++ ) + nVisible += aCol[nCol].VisibleCount( nRow1+1, nRow2 ); + + if ( nVisible > SCSIZE_MAX / sizeof(void*) ) + { + DBG_ERROR("zu viele Filterkritierien"); + nVisible = 0; + } + + SCSIZE nNewEntries = nVisible; + rQueryParam.Resize( nNewEntries ); + + SCSIZE nIndex = 0; + SCROW nRow = nRow1 + 1; + while (nRow <= nRow2) + { + nCol = nCol1; + while (nCol <= nCol2) + { + GetInputString( nCol, nRow, aCellStr ); + ScGlobal::pCharClass->toUpper( aCellStr ); + if (aCellStr.Len() > 0) + { + if (nIndex < nNewEntries) + { + rQueryParam.GetEntry(nIndex).nField = pFields[nCol - nCol1]; + rQueryParam.FillInExcelSyntax(aCellStr, nIndex); + nIndex++; + if (nIndex < nNewEntries) + rQueryParam.GetEntry(nIndex).eConnect = SC_AND; + } + else + bValid = FALSE; + } + nCol++; + } + nRow++; + if (nIndex < nNewEntries) + rQueryParam.GetEntry(nIndex).eConnect = SC_OR; + } + } + delete [] pFields; + return bValid; +} + +BOOL ScTable::CreateStarQuery(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) +{ + // A valid StarQuery must be at least 4 columns wide. To be precise it + // should be exactly 4 columns ... + // Additionally, if this wasn't checked, a formula pointing to a valid 1-3 + // column Excel style query range immediately left to itself would result + // in a circular reference when the field name or operator or value (first + // to third query range column) is obtained (#i58354#). Furthermore, if the + // range wasn't sufficiently specified data changes wouldn't flag formula + // cells for recalculation. + if (nCol2 - nCol1 < 3) + return FALSE; + + BOOL bValid; + BOOL bFound; + String aCellStr; + SCSIZE nIndex = 0; + SCROW nRow = nRow1; + DBG_ASSERT( rQueryParam.nTab != SCTAB_MAX, "rQueryParam.nTab no value, not bad but no good" ); + SCTAB nDBTab = (rQueryParam.nTab == SCTAB_MAX ? nTab : rQueryParam.nTab); + SCROW nDBRow1 = rQueryParam.nRow1; + SCCOL nDBCol2 = rQueryParam.nCol2; + + SCSIZE nNewEntries = static_cast<SCSIZE>(nRow2-nRow1+1); + rQueryParam.Resize( nNewEntries ); + + do + { + ScQueryEntry& rEntry = rQueryParam.GetEntry(nIndex); + + bValid = FALSE; + // Erste Spalte UND/ODER + if (nIndex > 0) + { + GetUpperCellString(nCol1, nRow, aCellStr); + if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_UND) ) + { + rEntry.eConnect = SC_AND; + bValid = TRUE; + } + else if ( aCellStr == ScGlobal::GetRscString(STR_TABLE_ODER) ) + { + rEntry.eConnect = SC_OR; + bValid = TRUE; + } + } + // Zweite Spalte FeldName + if ((nIndex < 1) || bValid) + { + bFound = FALSE; + GetUpperCellString(nCol1 + 1, nRow, aCellStr); + for (SCCOL i=rQueryParam.nCol1; (i <= nDBCol2) && (!bFound); i++) + { + String aFieldStr; + if ( nTab == nDBTab ) + GetUpperCellString(i, nDBRow1, aFieldStr); + else + pDocument->GetUpperCellString(i, nDBRow1, nDBTab, aFieldStr); + bFound = (aCellStr == aFieldStr); + if (bFound) + { + rEntry.nField = i; + bValid = TRUE; + } + else + bValid = FALSE; + } + } + // Dritte Spalte Operator =<>... + if (bValid) + { + bFound = FALSE; + GetUpperCellString(nCol1 + 2, nRow, aCellStr); + if (aCellStr.GetChar(0) == '<') + { + if (aCellStr.GetChar(1) == '>') + rEntry.eOp = SC_NOT_EQUAL; + else if (aCellStr.GetChar(1) == '=') + rEntry.eOp = SC_LESS_EQUAL; + else + rEntry.eOp = SC_LESS; + } + else if (aCellStr.GetChar(0) == '>') + { + if (aCellStr.GetChar(1) == '=') + rEntry.eOp = SC_GREATER_EQUAL; + else + rEntry.eOp = SC_GREATER; + } + else if (aCellStr.GetChar(0) == '=') + rEntry.eOp = SC_EQUAL; + + } + // Vierte Spalte Wert + if (bValid) + { + GetString(nCol1 + 3, nRow, *rEntry.pStr); + rEntry.bDoQuery = TRUE; + } + nIndex++; + nRow++; + } + while (bValid && (nRow <= nRow2) /* && (nIndex < MAXQUERY) */ ); + return bValid; +} + +BOOL ScTable::CreateQueryParam(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScQueryParam& rQueryParam) +{ + SCSIZE i, nCount; + PutInOrder(nCol1, nCol2); + PutInOrder(nRow1, nRow2); + + nCount = rQueryParam.GetEntryCount(); + for (i=0; i < nCount; i++) + rQueryParam.GetEntry(i).Clear(); + + // Standard QueryTabelle + BOOL bValid = CreateStarQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); + // Excel QueryTabelle + if (!bValid) + bValid = CreateExcelQuery(nCol1, nRow1, nCol2, nRow2, rQueryParam); + + nCount = rQueryParam.GetEntryCount(); + if (bValid) + { + // bQueryByString muss gesetzt sein + for (i=0; i < nCount; i++) + rQueryParam.GetEntry(i).bQueryByString = TRUE; + } + else + { + // nix + for (i=0; i < nCount; i++) + rQueryParam.GetEntry(i).Clear(); + } + return bValid; +} + +BOOL ScTable::HasColHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW /* nEndRow */ ) +{ + for (SCCOL nCol=nStartCol; nCol<=nEndCol; nCol++) + { + CellType eType = GetCellType( nCol, nStartRow ); + if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) + return FALSE; + } + return TRUE; +} + +BOOL ScTable::HasRowHeader( SCCOL nStartCol, SCROW nStartRow, SCCOL /* nEndCol */, SCROW nEndRow ) +{ + for (SCROW nRow=nStartRow; nRow<=nEndRow; nRow++) + { + CellType eType = GetCellType( nStartCol, nRow ); + if (eType != CELLTYPE_STRING && eType != CELLTYPE_EDIT) + return FALSE; + } + return TRUE; +} + +void ScTable::GetFilterEntries(SCCOL nCol, SCROW nRow1, SCROW nRow2, TypedScStrCollection& rStrings) +{ + aCol[nCol].GetFilterEntries( nRow1, nRow2, rStrings ); +} + +void ScTable::GetFilteredFilterEntries( SCCOL nCol, SCROW nRow1, SCROW nRow2, const ScQueryParam& rParam, TypedScStrCollection& rStrings ) +{ + // remove the entry for this column from the query parameter + ScQueryParam aParam( rParam ); + SCSIZE nEntryCount = aParam.GetEntryCount(); + for ( SCSIZE i = 0; i < nEntryCount && aParam.GetEntry(i).bDoQuery; ++i ) + { + ScQueryEntry& rEntry = aParam.GetEntry(i); + if ( rEntry.nField == nCol ) + { + aParam.DeleteQuery(i); + break; + } + } + nEntryCount = aParam.GetEntryCount(); + + BOOL* pSpecial = new BOOL[nEntryCount]; + lcl_PrepareQuery( pDocument, this, aParam, pSpecial ); + + for ( SCROW j = nRow1; j <= nRow2; ++j ) + { + if ( ValidQuery( j, aParam, pSpecial ) ) + { + aCol[nCol].GetFilterEntries( j, j, rStrings ); + } + } + + delete[] pSpecial; +} + +BOOL ScTable::GetDataEntries(SCCOL nCol, SCROW nRow, TypedScStrCollection& rStrings, BOOL bLimit) +{ + return aCol[nCol].GetDataEntries( nRow, rStrings, bLimit ); +} + +ULONG ScTable::GetCellCount() const +{ + ULONG nCellCount = 0; + + for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) + nCellCount += aCol[nCol].GetCellCount(); + + return nCellCount; +} + +ULONG ScTable::GetWeightedCount() const +{ + ULONG nCellCount = 0; + + for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) + if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline + nCellCount += aCol[nCol].GetWeightedCount(); + + return nCellCount; +} + +ULONG ScTable::GetCodeCount() const +{ + ULONG nCodeCount = 0; + + for ( SCCOL nCol=0; nCol<=MAXCOL; nCol++ ) + if ( aCol[nCol].GetCellCount() ) // GetCellCount ist inline + nCodeCount += aCol[nCol].GetCodeCount(); + + return nCodeCount; +} + +sal_Int32 ScTable::GetMaxStringLen( SCCOL nCol, SCROW nRowStart, + SCROW nRowEnd, CharSet eCharSet ) const +{ + if ( ValidCol(nCol) ) + return aCol[nCol].GetMaxStringLen( nRowStart, nRowEnd, eCharSet ); + else + return 0; +} + +xub_StrLen ScTable::GetMaxNumberStringLen( USHORT& nPrecision, SCCOL nCol, + SCROW nRowStart, SCROW nRowEnd ) const +{ + if ( ValidCol(nCol) ) + return aCol[nCol].GetMaxNumberStringLen( nPrecision, nRowStart, nRowEnd ); + else + return 0; +} + +void ScTable::UpdateSelectionFunction( ScFunctionData& rData, + SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + const ScMarkData& rMark ) +{ + // Cursor neben einer Markierung nicht beruecksichtigen: + //! nur noch MarkData uebergeben, Cursorposition ggf. hineinselektieren!!! + BOOL bSingle = ( rMark.IsMarked() || !rMark.IsMultiMarked() ); + + // Mehrfachselektion: + + SCCOL nCol; + if ( rMark.IsMultiMarked() ) + for (nCol=0; nCol<=MAXCOL && !rData.bError; nCol++) + if ( !pColFlags || !( pColFlags[nCol] & CR_HIDDEN ) ) + aCol[nCol].UpdateSelectionFunction( rMark, rData, pRowFlags, + bSingle && ( nCol >= nStartCol && nCol <= nEndCol ), + nStartRow, nEndRow ); + + // Einfachselektion (oder Cursor) nur wenn nicht negativ (und s.o.): + + if ( bSingle && !rMark.IsMarkNegative() ) + for (nCol=nStartCol; nCol<=nEndCol && !rData.bError; nCol++) + if ( !pColFlags || !( pColFlags[nCol] & CR_HIDDEN ) ) + aCol[nCol].UpdateAreaFunction( rData, pRowFlags, nStartRow, nEndRow ); +} + +void ScTable::FindConditionalFormat( ULONG nKey, ScRangeList& rList ) +{ + SCROW nStartRow = 0, nEndRow = 0; + for (SCCOL nCol=0; nCol<=MAXCOL; nCol++) + { + ScAttrIterator* pIter = aCol[nCol].CreateAttrIterator( 0, MAXROW ); + const ScPatternAttr* pPattern = pIter->Next( nStartRow, nEndRow ); + while (pPattern) + { + if (((SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() == nKey) + rList.Join( ScRange(nCol,nStartRow,nTab, nCol,nEndRow,nTab) ); + pPattern = pIter->Next( nStartRow, nEndRow ); + } + delete pIter; + } +} + + + + diff --git a/sc/source/core/data/table4.cxx b/sc/source/core/data/table4.cxx new file mode 100644 index 000000000000..fd725e96d124 --- /dev/null +++ b/sc/source/core/data/table4.cxx @@ -0,0 +1,1993 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table4.cxx,v $ + * $Revision: 1.23.126.4 $ + * + * 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" + +// System - Includes ----------------------------------------------------- + + + +#ifdef _MSC_VER +#pragma optimize("",off) + // sonst Absturz Win beim Fuellen +#endif + +// INCLUDE --------------------------------------------------------------- + +#include "scitems.hxx" +#include <svx/algitem.hxx> +#include <svx/boxitem.hxx> +#include <svx/brshitem.hxx> +#include <svx/cntritem.hxx> +#include <svx/colritem.hxx> +#include <svx/crsditem.hxx> +#include <svx/fhgtitem.hxx> +#include <svx/fontitem.hxx> +#include <svx/langitem.hxx> +#include <svx/postitem.hxx> +#include <svx/shdditem.hxx> +#include <svx/udlnitem.hxx> +#include <svx/wghtitem.hxx> +#include <svx/rotmodit.hxx> +#include <svx/editobj.hxx> +#include <svx/editeng.hxx> +#include <svx/eeitem.hxx> +#include <svx/escpitem.hxx> +#include <svtools/zforlist.hxx> +#include <vcl/keycodes.hxx> +#include <rtl/math.hxx> +#include <unotools/charclass.hxx> + +#include "attrib.hxx" +#include "patattr.hxx" +#include "cell.hxx" +#include "table.hxx" +#include "globstr.hrc" +#include "global.hxx" +#include "document.hxx" +#include "autoform.hxx" +#include "userlist.hxx" +#include "zforauto.hxx" +#include "subtotal.hxx" +#include "formula/errorcodes.hxx" +#include "rangenam.hxx" +#include "docpool.hxx" +#include "progress.hxx" + +#include <math.h> + +// STATIC DATA ----------------------------------------------------------- + +#define _D_MAX_LONG_ (double) 0x7fffffff + +extern USHORT nScFillModeMouseModifier; // global.cxx + +// ----------------------------------------------------------------------- + +short lcl_DecompValueString( String& aValue, sal_Int32& nVal, USHORT* pMinDigits = NULL ) +{ + if ( !aValue.Len() ) + { + nVal = 0; + return 0; + } + const sal_Unicode* p = aValue.GetBuffer(); + xub_StrLen nNeg = 0; + xub_StrLen nNum = 0; + if ( p[nNum] == '-' ) + nNum = nNeg = 1; + while ( p[nNum] && CharClass::isAsciiNumeric( p[nNum] ) ) + nNum++; + + sal_Unicode cNext = p[nNum]; // 0 if at the end + sal_Unicode cLast = p[aValue.Len()-1]; + + // #i5550# If there are numbers at the beginning and the end, + // prefer the one at the beginning only if it's followed by a space. + // Otherwise, use the number at the end, to enable things like IP addresses. + if ( nNum > nNeg && ( cNext == 0 || cNext == ' ' || !CharClass::isAsciiNumeric(cLast) ) ) + { // number at the beginning + nVal = aValue.Copy( 0, nNum ).ToInt32(); + // #60893# any number with a leading zero sets the minimum number of digits + if ( p[nNeg] == '0' && pMinDigits && ( nNum - nNeg > *pMinDigits ) ) + *pMinDigits = nNum - nNeg; + aValue.Erase( 0, nNum ); + return -1; + } + else + { + nNeg = 0; + xub_StrLen nEnd = nNum = aValue.Len() - 1; + while ( nNum && CharClass::isAsciiNumeric( p[nNum] ) ) + nNum--; + if ( p[nNum] == '-' ) + { + nNum--; + nNeg = 1; + } + if ( nNum < nEnd - nNeg ) + { // number at the end + nVal = aValue.Copy( nNum + 1 ).ToInt32(); + // #60893# any number with a leading zero sets the minimum number of digits + if ( p[nNum+1+nNeg] == '0' && pMinDigits && ( nEnd - nNum - nNeg > *pMinDigits ) ) + *pMinDigits = nEnd - nNum - nNeg; + aValue.Erase( nNum + 1 ); + return 1; + } + } + nVal = 0; + return 0; +} + +String lcl_ValueString( sal_Int32 nValue, USHORT nMinDigits ) +{ + if ( nMinDigits <= 1 ) + return String::CreateFromInt32( nValue ); // simple case... + else + { + String aStr = String::CreateFromInt32( Abs( nValue ) ); + if ( aStr.Len() < nMinDigits ) + { + String aZero; + aZero.Fill( nMinDigits - aStr.Len(), '0' ); + aStr.Insert( aZero, 0 ); + } + // nMinDigits doesn't include the '-' sign -> add after inserting zeros + if ( nValue < 0 ) + aStr.Insert( '-', 0 ); + return aStr; + } +} + +static ScBaseCell * lcl_getSuffixCell( ScDocument* pDocument, sal_Int32 nValue, + USHORT nDigits, const String& rSuffix, CellType eCellType, + BOOL bIsOrdinalSuffix ) +{ + String aValue( lcl_ValueString( nValue, nDigits )); + if (!bIsOrdinalSuffix) + return new ScStringCell( aValue += rSuffix); + + String aOrdinalSuffix( ScGlobal::GetOrdinalSuffix( nValue)); + if (eCellType != CELLTYPE_EDIT) + return new ScStringCell( aValue += aOrdinalSuffix); + + EditEngine aEngine( pDocument->GetEnginePool() ); + SfxItemSet aAttr = aEngine.GetEmptyItemSet(); + aAttr.Put( SvxEscapementItem( SVX_ESCAPEMENT_SUPERSCRIPT, EE_CHAR_ESCAPEMENT)); + aEngine.SetText( aValue ); + aEngine.QuickInsertText( aOrdinalSuffix, ESelection( 0, aValue.Len(), 0, + aValue.Len() + aOrdinalSuffix.Len())); + aEngine.QuickSetAttribs( aAttr, ESelection( 0, aValue.Len(), 0, aValue.Len() + + aOrdinalSuffix.Len())); + return new ScEditCell( aEngine.CreateTextObject(), pDocument, NULL ); +} + +void ScTable::FillAnalyse( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + FillCmd& rCmd, FillDateCmd& rDateCmd, + double& rInc, USHORT& rMinDigits, + ScUserListData*& rListData, USHORT& rListIndex) +{ + DBG_ASSERT( nCol1==nCol2 || nRow1==nRow2, "FillAnalyse: falscher Bereich" ); + + rInc = 0.0; + rMinDigits = 0; + rListData = NULL; + rCmd = FILL_SIMPLE; + if ( nScFillModeMouseModifier & KEY_MOD1 ) + return ; // Ctrl-Taste: Copy + + SCCOL nAddX; + SCROW nAddY; + SCSIZE nCount; + if (nCol1 == nCol2) + { + nAddX = 0; + nAddY = 1; + nCount = static_cast<SCSIZE>(nRow2 - nRow1 + 1); + } + else + { + nAddX = 1; + nAddY = 0; + nCount = static_cast<SCSIZE>(nCol2 - nCol1 + 1); + } + + SCCOL nCol = nCol1; + SCROW nRow = nRow1; + + ScBaseCell* pFirstCell = GetCell( nCol, nRow ); + CellType eCellType = pFirstCell ? pFirstCell->GetCellType() : CELLTYPE_NONE; + + if (eCellType == CELLTYPE_VALUE) + { + UINT32 nFormat = ((const SfxUInt32Item*)GetAttr(nCol,nRow,ATTR_VALUE_FORMAT))->GetValue(); + BOOL bDate = ( pDocument->GetFormatTable()->GetType(nFormat) == NUMBERFORMAT_DATE ); + if (bDate) + { + if (nCount > 1) + { + long nCmpInc = 0; + double nVal; + Date aNullDate = *pDocument->GetFormatTable()->GetNullDate(); + Date aDate1 = aNullDate; + nVal = ((ScValueCell*)pFirstCell)->GetValue(); + aDate1 += (long)nVal; + Date aDate2 = aNullDate; + nVal = GetValue(nCol+nAddX, nRow+nAddY); + aDate2 += (long)nVal; + if ( aDate1 != aDate2 ) + { + FillDateCmd eType; + long nDDiff = aDate2.GetDay() - (long) aDate1.GetDay(); + long nMDiff = aDate2.GetMonth() - (long) aDate1.GetMonth(); + long nYDiff = aDate2.GetYear() - (long) aDate1.GetYear(); + if ( nDDiff ) + { + eType = FILL_DAY; + nCmpInc = aDate2 - aDate1; + } + else + { + eType = FILL_MONTH; + nCmpInc = nMDiff + 12 * nYDiff; + } + + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + BOOL bVal = TRUE; + for (USHORT i=1; i<nCount && bVal; i++) + { + ScBaseCell* pCell = GetCell(nCol,nRow); + if (pCell && pCell->GetCellType() == CELLTYPE_VALUE) + { + nVal = ((ScValueCell*)pCell)->GetValue(); + aDate2 = aNullDate + (long) nVal; + if ( eType == FILL_DAY ) + { + if ( aDate2-aDate1 != nCmpInc ) + bVal = FALSE; + } + else + { + nDDiff = aDate2.GetDay() - (long) aDate1.GetDay(); + nMDiff = aDate2.GetMonth() - (long) aDate1.GetMonth(); + nYDiff = aDate2.GetYear() - (long) aDate1.GetYear(); + if (nDDiff || ( nMDiff + 12 * nYDiff != nCmpInc )) + bVal = FALSE; + } + aDate1 = aDate2; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + } + else + bVal = FALSE; // #50965# kein Datum passt auch nicht + } + if (bVal) + { + if ( eType == FILL_MONTH && ( nCmpInc % 12 == 0 ) ) + { + eType = FILL_YEAR; + nCmpInc /= 12; + } + rCmd = FILL_DATE; + rDateCmd = eType; + rInc = nCmpInc; + } + } + } + else // einzelnes Datum -> Tage hochzaehlen + { + rCmd = FILL_DATE; + rDateCmd = FILL_DAY; + rInc = 1.0; + } + } + else + { + if (nCount > 1) + { + double nVal1 = ((ScValueCell*)pFirstCell)->GetValue(); + double nVal2 = GetValue(nCol+nAddX, nRow+nAddY); + rInc = nVal2 - nVal1; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + BOOL bVal = TRUE; + for (USHORT i=1; i<nCount && bVal; i++) + { + ScBaseCell* pCell = GetCell(nCol,nRow); + if (pCell && pCell->GetCellType() == CELLTYPE_VALUE) + { + nVal2 = ((ScValueCell*)pCell)->GetValue(); + double nDiff = nVal2 - nVal1; + if ( !::rtl::math::approxEqual( nDiff, rInc ) ) + bVal = FALSE; + nVal1 = nVal2; + } + else + bVal = FALSE; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + } + if (bVal) + rCmd = FILL_LINEAR; + } + } + } + else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) + { + String aStr; + GetString(nCol, nRow, aStr); + rListData = (ScUserListData*)(ScGlobal::GetUserList()->GetData(aStr)); + if (rListData) + { + rListData->GetSubIndex(aStr, rListIndex); + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + for (USHORT i=1; i<nCount && rListData; i++) + { + GetString(nCol, nRow, aStr); + if (!rListData->GetSubIndex(aStr, rListIndex)) + rListData = NULL; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + } + } + else if ( nCount > 1 ) + { + // pass rMinDigits to all DecompValueString calls + // -> longest number defines rMinDigits + + sal_Int32 nVal1; + short nFlag1 = lcl_DecompValueString( aStr, nVal1, &rMinDigits ); + if ( nFlag1 ) + { + sal_Int32 nVal2; + GetString( nCol+nAddX, nRow+nAddY, aStr ); + short nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits ); + if ( nFlag1 == nFlag2 ) + { + rInc = (double)nVal2 - (double)nVal1; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + BOOL bVal = TRUE; + for (USHORT i=1; i<nCount && bVal; i++) + { + ScBaseCell* pCell = GetCell(nCol,nRow); + CellType eType = pCell ? pCell->GetCellType() : CELLTYPE_NONE; + if ( eType == CELLTYPE_STRING || eType == CELLTYPE_EDIT ) + { + if ( eType == CELLTYPE_STRING ) + ((ScStringCell*)pCell)->GetString( aStr ); + else + ((ScEditCell*)pCell)->GetString( aStr ); + nFlag2 = lcl_DecompValueString( aStr, nVal2, &rMinDigits ); + if ( nFlag1 == nFlag2 ) + { + double nDiff = (double)nVal2 - (double)nVal1; + if ( !::rtl::math::approxEqual( nDiff, rInc ) ) + bVal = FALSE; + nVal1 = nVal2; + } + else + bVal = FALSE; + } + else + bVal = FALSE; + nCol = sal::static_int_cast<SCCOL>( nCol + nAddX ); + nRow = sal::static_int_cast<SCROW>( nRow + nAddY ); + } + if (bVal) + rCmd = FILL_LINEAR; + } + } + } + else + { + // call DecompValueString to set rMinDigits + sal_Int32 nDummy; + lcl_DecompValueString( aStr, nDummy, &rMinDigits ); + } + } +} + +void ScTable::FillFormula(ULONG& /* nFormulaCounter */, BOOL /* bFirst */, ScFormulaCell* pSrcCell, + SCCOL nDestCol, SCROW nDestRow, BOOL bLast ) +{ +/* USHORT nTokArrLen = pSrcCell->GetTokenArrayLen(); + if ( nTokArrLen > 15 ) // mehr als =A1 oder =67 + { + ScRangeName* pRangeName = pDocument->GetRangeName(); + String aName("___SC_"); // Wird dieser String veraendert, + // auch in document2 EraseNonUsed... + // mitaendern!! + aName += pRangeName->GetSharedMaxIndex() + 1; + aName += '_'; + aName += nFormulaCounter; + nFormulaCounter++; + if (bFirst) + { + ScRangeData *pAktRange = new ScRangeData( + pDocument, aName, pSrcCell->GetTokenArray(), nTokArrLen, + pSrcCell->GetCol(), pSrcCell->GetRow(), nTab ,RT_SHARED); + if (!pRangeName->Insert( pAktRange )) + delete pAktRange; + else + bSharedNameInserted = TRUE; + } + USHORT nIndex; + pRangeName->SearchName(aName, nIndex); + if (!pRangeName) + { + DBG_ERROR("ScTable::FillFormula: Falscher Name"); + return; + } + nIndex = ((ScRangeData*) ((*pRangeName)[nIndex]))->GetIndex(); + ScTokenArray aArr; + aArr.AddName(nIndex); + aArr.AddOpCode(ocStop); + ScFormulaCell* pDestCell = new ScFormulaCell + (pDocument, ScAddress( nDestCol, nDestRow, nTab ), aArr ); + aCol[nDestCol].Insert(nDestRow, pDestCell); + } + else +*/ { + pDocument->SetNoListening( TRUE ); // noch falsche Referenzen + ScAddress aAddr( nDestCol, nDestRow, nTab ); + ScFormulaCell* pDestCell = new ScFormulaCell( *pSrcCell, *pDocument, aAddr ); + aCol[nDestCol].Insert(nDestRow, pDestCell); +#if 0 +// mit RelRefs unnoetig + pDestCell->UpdateReference(URM_COPY, + ScRange( aAddr, aAddr ), + nDestCol - pSrcCell->aPos.Col(), + nDestRow - pSrcCell->aPos.Row(), 0); +#endif + if ( bLast && pDestCell->GetMatrixFlag() ) + { + ScAddress aOrg; + if ( pDestCell->GetMatrixOrigin( aOrg ) ) + { + if ( nDestCol >= aOrg.Col() && nDestRow >= aOrg.Row() ) + { + ScBaseCell* pOrgCell = pDocument->GetCell( aOrg ); + if ( pOrgCell && pOrgCell->GetCellType() == CELLTYPE_FORMULA + && ((ScFormulaCell*)pOrgCell)->GetMatrixFlag() == MM_FORMULA ) + { + ((ScFormulaCell*)pOrgCell)->SetMatColsRows( + nDestCol - aOrg.Col() + 1, + nDestRow - aOrg.Row() + 1 ); + } + else + { + DBG_ERRORFILE( "FillFormula: MatrixOrigin keine Formelzelle mit MM_FORMULA" ); + } + } + else + { + DBG_ERRORFILE( "FillFormula: MatrixOrigin rechts unten" ); + } + } + else + { + DBG_ERRORFILE( "FillFormula: kein MatrixOrigin" ); + } + } + pDocument->SetNoListening( FALSE ); + pDestCell->StartListeningTo( pDocument ); + } +} + +void ScTable::FillAuto( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ULONG nFillCount, FillDir eFillDir, ScProgress& rProgress ) +{ + if ( (nFillCount == 0) || !ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2) ) + return; + + // + // Richtung auswerten + // + + BOOL bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP); + BOOL bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT); + + ULONG nCol = 0; + ULONG nRow = 0; + ULONG& rInner = bVertical ? nRow : nCol; // Schleifenvariablen + ULONG& rOuter = bVertical ? nCol : nRow; + ULONG nOStart; + ULONG nOEnd; + ULONG nIStart; + ULONG nIEnd; + ULONG nISrcStart; + ULONG nISrcEnd; + + if (bVertical) + { + nOStart = nCol1; + nOEnd = nCol2; + if (bPositive) + { + nISrcStart = nRow1; + nISrcEnd = nRow2; + nIStart = nRow2 + 1; + nIEnd = nRow2 + nFillCount; + } + else + { + nISrcStart = nRow2; + nISrcEnd = nRow1; + nIStart = nRow1 - 1; + nIEnd = nRow1 - nFillCount; + } + } + else + { + nOStart = nRow1; + nOEnd = nRow2; + if (bPositive) + { + nISrcStart = nCol1; + nISrcEnd = nCol2; + nIStart = nCol2 + 1; + nIEnd = nCol2 + nFillCount; + } + else + { + nISrcStart = nCol2; + nISrcEnd = nCol1; + nIStart = nCol1 - 1; + nIEnd = nCol1 - nFillCount; + } + } + ULONG nIMin = nIStart; + ULONG nIMax = nIEnd; + PutInOrder(nIMin,nIMax); + if (bVertical) + DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), IDF_AUTOFILL); + else + DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, IDF_AUTOFILL); + + ULONG nProgress = rProgress.GetState(); + + // + // ausfuehren + // + + ULONG nActFormCnt = 0; + for (rOuter = nOStart; rOuter <= nOEnd; rOuter++) + { + ULONG nMaxFormCnt = 0; // fuer Formeln + + // Attributierung uebertragen + + const ScPatternAttr* pSrcPattern = NULL; + ULONG nAtSrc = nISrcStart; + ScPatternAttr* pNewPattern = NULL; + BOOL bGetPattern = TRUE; + rInner = nIStart; + while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + { + const ScStyleSheet* pStyleSheet = NULL; + if ( bGetPattern ) + { + if ( pNewPattern ) + delete pNewPattern; + if (bVertical) // rInner&:=nRow, rOuter&:=nCol + pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nAtSrc)); + else // rInner&:=nCol, rOuter&:=nRow + pSrcPattern = aCol[nAtSrc].GetPattern(static_cast<SCROW>(nRow)); + bGetPattern = FALSE; + pStyleSheet = pSrcPattern->GetStyleSheet(); + // Merge/Mergeflag nicht uebernehmen, + const SfxItemSet& rSet = pSrcPattern->GetItemSet(); + if ( rSet.GetItemState(ATTR_MERGE, FALSE) == SFX_ITEM_SET + || rSet.GetItemState(ATTR_MERGE_FLAG, FALSE) == SFX_ITEM_SET ) + { + pNewPattern = new ScPatternAttr( *pSrcPattern ); + SfxItemSet& rNewSet = pNewPattern->GetItemSet(); + rNewSet.ClearItem(ATTR_MERGE); + rNewSet.ClearItem(ATTR_MERGE_FLAG); + } + else + pNewPattern = NULL; + } + + if ( bVertical && nISrcStart == nISrcEnd ) + { + // Attribute komplett am Stueck setzen + if (pNewPattern || pSrcPattern != pDocument->GetDefPattern()) + { + // Default steht schon da (DeleteArea) + SCROW nY1 = static_cast<SCROW>(Min( nIStart, nIEnd )); + SCROW nY2 = static_cast<SCROW>(Max( nIStart, nIEnd )); + if ( pStyleSheet ) + aCol[nCol].ApplyStyleArea( nY1, nY2, *pStyleSheet ); + if ( pNewPattern ) + aCol[nCol].ApplyPatternArea( nY1, nY2, *pNewPattern ); + else + aCol[nCol].ApplyPatternArea( nY1, nY2, *pSrcPattern ); + } + break; // Schleife abbrechen + } + + if ( pSrcPattern != aCol[nCol].GetPattern( static_cast<SCROW>(nRow) ) ) + { + // Vorlage auch uebernehmen + //! am AttrArray mit ApplyPattern zusammenfassen ?? + if ( pStyleSheet ) + aCol[nCol].ApplyStyle( static_cast<SCROW>(nRow), *pStyleSheet ); + + // ApplyPattern statt SetPattern um alte MergeFlags stehenzulassen + if ( pNewPattern ) + aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pNewPattern ); + else + aCol[nCol].ApplyPattern( static_cast<SCROW>(nRow), *pSrcPattern ); + } + + if (nAtSrc==nISrcEnd) + { + if ( nAtSrc != nISrcStart ) + { // mehr als eine Source-Zelle + nAtSrc = nISrcStart; + bGetPattern = TRUE; + } + } + else if (bPositive) + { + ++nAtSrc; + bGetPattern = TRUE; + } + else + { + --nAtSrc; + bGetPattern = TRUE; + } + + if (rInner == nIEnd) break; + if (bPositive) ++rInner; else --rInner; + } + if ( pNewPattern ) + delete pNewPattern; + + // Analyse + + FillCmd eFillCmd; + FillDateCmd eDateCmd; + double nInc; + USHORT nMinDigits; + ScUserListData* pListData = NULL; + USHORT nListIndex; + if (bVertical) + FillAnalyse(static_cast<SCCOL>(nCol),nRow1, + static_cast<SCCOL>(nCol),nRow2, eFillCmd,eDateCmd, + nInc,nMinDigits, pListData,nListIndex); + else + FillAnalyse(nCol1,static_cast<SCROW>(nRow), + nCol2,static_cast<SCROW>(nRow), eFillCmd,eDateCmd, + nInc,nMinDigits, pListData,nListIndex); + + if (bVertical) + aCol[nCol].Resize( aCol[nCol].GetCellCount() + nFillCount ); + + if (pListData) + { + USHORT nListCount = pListData->GetSubCount(); + if ( !bPositive ) + { + // nListIndex auf FillAnalyse zeigt auf den letzten Eintrag -> anpassen + ULONG nSub = nISrcStart - nISrcEnd; + for (ULONG i=0; i<nSub; i++) + { + if (nListIndex == 0) nListIndex = nListCount; + --nListIndex; + } + } + + rInner = nIStart; + while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + { + if (bPositive) + { + ++nListIndex; + if (nListIndex >= nListCount) nListIndex = 0; + } + else + { + if (nListIndex == 0) nListIndex = nListCount; + --nListIndex; + } + aCol[nCol].Insert(static_cast<SCROW>(nRow), new ScStringCell(pListData->GetSubStr(nListIndex))); + + if (rInner == nIEnd) break; + if (bPositive) ++rInner; else --rInner; + } + nProgress += nIMax - nIMin + 1; + rProgress.SetStateOnPercent( nProgress ); + } + else if (eFillCmd == FILL_SIMPLE) // Auffuellen mit Muster + { + ULONG nSource = nISrcStart; + double nDelta; + if ( nScFillModeMouseModifier & KEY_MOD1 ) + nDelta = 0.0; + else if ( bPositive ) + nDelta = 1.0; + else + nDelta = -1.0; + double nVal = 0.0; + ULONG nFormulaCounter = nActFormCnt; + BOOL bFirst = TRUE; + BOOL bGetCell = TRUE; + USHORT nCellDigits = 0; + short nHeadNoneTail = 0; + sal_Int32 nStringValue = 0; + String aValue; + ScBaseCell* pSrcCell = NULL; + CellType eCellType = CELLTYPE_NONE; + BOOL bIsOrdinalSuffix = FALSE; + + rInner = nIStart; + while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + { + if ( bGetCell ) + { + if (bVertical) // rInner&:=nRow, rOuter&:=nCol + pSrcCell = aCol[nCol].GetCell( static_cast<SCROW>(nSource) ); + else // rInner&:=nCol, rOuter&:=nRow + pSrcCell = aCol[nSource].GetCell( static_cast<SCROW>(nRow) ); + bGetCell = FALSE; + if ( pSrcCell ) + { + eCellType = pSrcCell->GetCellType(); + switch ( eCellType ) + { + case CELLTYPE_VALUE: + nVal = ((ScValueCell*)pSrcCell)->GetValue(); + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + if ( eCellType == CELLTYPE_STRING ) + ((ScStringCell*)pSrcCell)->GetString( aValue ); + else + ((ScEditCell*)pSrcCell)->GetString( aValue ); + if ( !(nScFillModeMouseModifier & KEY_MOD1) ) + { + nCellDigits = 0; // look at each source cell individually + nHeadNoneTail = lcl_DecompValueString( + aValue, nStringValue, &nCellDigits ); + + bIsOrdinalSuffix = aValue.Equals( + ScGlobal::GetOrdinalSuffix( nStringValue)); + } + break; + default: + { + // added to avoid warnings + } + } + } + else + eCellType = CELLTYPE_NONE; + } + switch (eCellType) + { + case CELLTYPE_VALUE: + aCol[nCol].Insert(static_cast<SCROW>(nRow), new ScValueCell(nVal + nDelta)); + break; + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + if ( nHeadNoneTail ) + { + // #i48009# with the "nStringValue+(long)nDelta" expression within the + // lcl_ValueString calls, gcc 3.4.1 makes wrong optimizations (ok in 3.4.3), + // so nNextValue is now calculated ahead. + sal_Int32 nNextValue = nStringValue+(sal_Int32)nDelta; + + String aStr; + if ( nHeadNoneTail < 0 ) + { + aCol[nCol].Insert( static_cast<SCROW>(nRow), + lcl_getSuffixCell( pDocument, + nNextValue, nCellDigits, aValue, + eCellType, bIsOrdinalSuffix)); + } + else + { + aStr = aValue; + aStr += lcl_ValueString( nNextValue, nCellDigits ); + aCol[nCol].Insert( static_cast<SCROW>(nRow), + new ScStringCell( aStr)); + } + } + else + { + ScAddress aDestPos( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), nTab ); + switch ( eCellType ) + { + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + aCol[nCol].Insert( aDestPos.Row(), pSrcCell->CloneWithoutNote( *pDocument ) ); + break; + default: + { + // added to avoid warnings + } + } + } + break; + case CELLTYPE_FORMULA : + FillFormula( nFormulaCounter, bFirst, + (ScFormulaCell*) pSrcCell, + static_cast<SCCOL>(nCol), + static_cast<SCROW>(nRow), (rInner == nIEnd) ); + if (nFormulaCounter - nActFormCnt > nMaxFormCnt) + nMaxFormCnt = nFormulaCounter - nActFormCnt; + break; + default: + { + // added to avoid warnings + } + } + + if (nSource==nISrcEnd) + { + if ( nSource != nISrcStart ) + { // mehr als eine Source-Zelle + nSource = nISrcStart; + bGetCell = TRUE; + } + if ( !(nScFillModeMouseModifier & KEY_MOD1) ) + { + if ( bPositive ) + nDelta += 1.0; + else + nDelta -= 1.0; + } + nFormulaCounter = nActFormCnt; + bFirst = FALSE; + } + else if (bPositive) + { + ++nSource; + bGetCell = TRUE; + } + else + { + --nSource; + bGetCell = TRUE; + } + + // Progress in der inneren Schleife nur bei teuren Zellen, + // und auch dann nicht fuer jede einzelne + + ++nProgress; + if ( eCellType == CELLTYPE_FORMULA || eCellType == CELLTYPE_EDIT ) + rProgress.SetStateOnPercent( nProgress ); + + if (rInner == nIEnd) break; + if (bPositive) ++rInner; else --rInner; + } + rProgress.SetStateOnPercent( nProgress ); + } + else + { + if (!bPositive) + nInc = -nInc; + double nEndVal = (nInc>=0.0) ? MAXDOUBLE : -MAXDOUBLE; + if (bVertical) + FillSeries( static_cast<SCCOL>(nCol), nRow1, + static_cast<SCCOL>(nCol), nRow2, nFillCount, eFillDir, + eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, FALSE, + rProgress ); + else + FillSeries( nCol1, static_cast<SCROW>(nRow), nCol2, + static_cast<SCROW>(nRow), nFillCount, eFillDir, + eFillCmd, eDateCmd, nInc, nEndVal, nMinDigits, FALSE, + rProgress ); + nProgress = rProgress.GetState(); + } + + nActFormCnt += nMaxFormCnt; + } +} + +String ScTable::GetAutoFillPreview( const ScRange& rSource, SCCOL nEndX, SCROW nEndY ) +{ + String aValue; + + SCCOL nCol1 = rSource.aStart.Col(); + SCROW nRow1 = rSource.aStart.Row(); + SCCOL nCol2 = rSource.aEnd.Col(); + SCROW nRow2 = rSource.aEnd.Row(); + BOOL bOk = TRUE; + long nIndex = 0; + ULONG nSrcCount = 0; + FillDir eFillDir = FILL_TO_BOTTOM; + if ( nEndX == nCol2 && nEndY == nRow2 ) // leer + bOk = FALSE; + else if ( nEndX == nCol2 ) // nach oben/unten + { + nEndX = nCol2 = nCol1; // nur erste Spalte ansehen + nSrcCount = nRow2 - nRow1 + 1; + nIndex = ((long)nEndY) - nRow1; // kann negativ werden + if ( nEndY >= nRow1 ) + eFillDir = FILL_TO_BOTTOM; + else + eFillDir = FILL_TO_TOP; + } + else if ( nEndY == nRow2 ) // nach links/rechts + { + nEndY = nRow2 = nRow1; // nur erste Zeile ansehen + nSrcCount = nCol2 - nCol1 + 1; + nIndex = ((long)nEndX) - nCol1; // kann negativ werden + if ( nEndX >= nCol1 ) + eFillDir = FILL_TO_RIGHT; + else + eFillDir = FILL_TO_LEFT; + } + else // Richtung nicht eindeutig + bOk = FALSE; + + if ( bOk ) + { + FillCmd eFillCmd; + FillDateCmd eDateCmd; + double nInc; + USHORT nMinDigits; + ScUserListData* pListData = NULL; + USHORT nListIndex; + + FillAnalyse(nCol1,nRow1, nCol2,nRow2, eFillCmd,eDateCmd, nInc,nMinDigits, pListData,nListIndex); + + if ( pListData ) // benutzerdefinierte Liste + { + USHORT nListCount = pListData->GetSubCount(); + if ( nListCount ) + { + ULONG nSub = nSrcCount - 1; // nListIndex ist vom letzten Source-Eintrag + while ( nIndex < sal::static_int_cast<long>(nSub) ) + nIndex += nListCount; + ULONG nPos = ( nListIndex + nIndex - nSub ) % nListCount; + aValue = pListData->GetSubStr(sal::static_int_cast<USHORT>(nPos)); + } + } + else if ( eFillCmd == FILL_SIMPLE ) // Auffuellen mit Muster + { + long nPosIndex = nIndex; + while ( nPosIndex < 0 ) + nPosIndex += nSrcCount; + ULONG nPos = nPosIndex % nSrcCount; + SCCOL nSrcX = nCol1; + SCROW nSrcY = nRow1; + if ( eFillDir == FILL_TO_TOP || eFillDir == FILL_TO_BOTTOM ) + nSrcY = sal::static_int_cast<SCROW>( nSrcY + static_cast<SCROW>(nPos) ); + else + nSrcX = sal::static_int_cast<SCCOL>( nSrcX + static_cast<SCCOL>(nPos) ); + + ScBaseCell* pCell = GetCell( nSrcX, nSrcY ); + if ( pCell ) + { + sal_Int32 nDelta; + if (nIndex >= 0) + nDelta = nIndex / nSrcCount; + else + nDelta = ( nIndex - nSrcCount + 1 ) / nSrcCount; // -1 -> -1 + + CellType eType = pCell->GetCellType(); + switch ( eType ) + { + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + if ( eType == CELLTYPE_STRING ) + ((ScStringCell*)pCell)->GetString( aValue ); + else + ((ScEditCell*)pCell)->GetString( aValue ); + if ( !(nScFillModeMouseModifier & KEY_MOD1) ) + { + sal_Int32 nVal; + USHORT nCellDigits = 0; // look at each source cell individually + short nFlag = lcl_DecompValueString( aValue, nVal, &nCellDigits ); + if ( nFlag < 0 ) + { + if (aValue.Equals( ScGlobal::GetOrdinalSuffix( nVal))) + aValue = ScGlobal::GetOrdinalSuffix( nVal + nDelta); + + aValue.Insert( lcl_ValueString( nVal + nDelta, nCellDigits ), 0 ); + } + else if ( nFlag > 0 ) + aValue += lcl_ValueString( nVal + nDelta, nCellDigits ); + } + } + break; + case CELLTYPE_VALUE: + { + // dabei kann's keinen Ueberlauf geben... + double nVal = ((ScValueCell*)pCell)->GetValue(); + if ( !(nScFillModeMouseModifier & KEY_MOD1) ) + nVal += (double) nDelta; + + Color* pColor; + ULONG nNumFmt = GetNumberFormat( nSrcX, nSrcY ); + pDocument->GetFormatTable()-> + GetOutputString( nVal, nNumFmt, aValue, &pColor ); + } + break; + // Formeln nicht + default: + { + // added to avoid warnings + } + } + } + } + else if ( eFillCmd == FILL_LINEAR || eFillCmd == FILL_DATE ) // Werte + { + BOOL bValueOk; + double nStart; + sal_Int32 nVal = 0; + short nHeadNoneTail = 0; + ScBaseCell* pCell = GetCell( nCol1, nRow1 ); + if ( pCell ) + { + CellType eType = pCell->GetCellType(); + switch ( eType ) + { + case CELLTYPE_STRING: + case CELLTYPE_EDIT: + { + if ( eType == CELLTYPE_STRING ) + ((ScStringCell*)pCell)->GetString( aValue ); + else + ((ScEditCell*)pCell)->GetString( aValue ); + nHeadNoneTail = lcl_DecompValueString( aValue, nVal ); + if ( nHeadNoneTail ) + nStart = (double)nVal; + else + nStart = 0.0; + } + break; + case CELLTYPE_VALUE: + nStart = ((ScValueCell*)pCell)->GetValue(); + break; + case CELLTYPE_FORMULA: + nStart = ((ScFormulaCell*)pCell)->GetValue(); + break; + default: + nStart = 0.0; + } + } + else + nStart = 0.0; + if ( eFillCmd == FILL_LINEAR ) + { + double nAdd = nInc; + bValueOk = ( SubTotal::SafeMult( nAdd, (double) nIndex ) && + SubTotal::SafePlus( nStart, nAdd ) ); + } + else // Datum + { + bValueOk = TRUE; + USHORT nDayOfMonth = 0; + if ( nIndex < 0 ) + { + nIndex = -nIndex; + nInc = -nInc; + } + for (long i=0; i<nIndex; i++) + IncDate( nStart, nDayOfMonth, nInc, eDateCmd ); + } + + if (bValueOk) + { + if ( nHeadNoneTail ) + { + if ( nHeadNoneTail < 0 ) + { + if (aValue.Equals( ScGlobal::GetOrdinalSuffix( nVal))) + aValue = ScGlobal::GetOrdinalSuffix( (sal_Int32)nStart ); + + aValue.Insert( lcl_ValueString( (sal_Int32)nStart, nMinDigits ), 0 ); + } + else + aValue += lcl_ValueString( (sal_Int32)nStart, nMinDigits ); + } + else + { + //! Zahlformat je nach Index holen? + Color* pColor; + ULONG nNumFmt = GetNumberFormat( nCol1, nRow1 ); + pDocument->GetFormatTable()-> + GetOutputString( nStart, nNumFmt, aValue, &pColor ); + } + } + } + else + { + DBG_ERROR("GetAutoFillPreview: falscher Modus"); + } + } + + return aValue; +} + +void ScTable::IncDate(double& rVal, USHORT& nDayOfMonth, double nStep, FillDateCmd eCmd) +{ + if (eCmd == FILL_DAY) + { + rVal += nStep; + return; + } + + // class Date Grenzen + const USHORT nMinYear = 1583; + const USHORT nMaxYear = 9956; + + long nInc = (long) nStep; // nach oben/unten begrenzen ? + Date aNullDate = *pDocument->GetFormatTable()->GetNullDate(); + Date aDate = aNullDate; + aDate += (long)rVal; + switch (eCmd) + { + case FILL_WEEKDAY: + { + aDate += nInc; + DayOfWeek eWeekDay = aDate.GetDayOfWeek(); + if (nInc >= 0) + { + if (eWeekDay == SATURDAY) + aDate += 2; + else if (eWeekDay == SUNDAY) + aDate += 1; + } + else + { + if (eWeekDay == SATURDAY) + aDate -= 1; + else if (eWeekDay == SUNDAY) + aDate -= 2; + } + } + break; + case FILL_MONTH: + { + if ( nDayOfMonth == 0 ) + nDayOfMonth = aDate.GetDay(); // init + long nMonth = aDate.GetMonth(); + long nYear = aDate.GetYear(); + + nMonth += nInc; + + if (nInc >= 0) + { + if (nMonth > 12) + { + long nYAdd = (nMonth-1) / 12; + nMonth -= nYAdd * 12; + nYear += nYAdd; + } + } + else + { + if (nMonth < 1) + { + long nYAdd = 1 - nMonth / 12; // positiv + nMonth += nYAdd * 12; + nYear -= nYAdd; + } + } + + if ( nYear < nMinYear ) + aDate = Date( 1,1, nMinYear ); + else if ( nYear > nMaxYear ) + aDate = Date( 31,12, nMaxYear ); + else + { + aDate.SetMonth((USHORT) nMonth); + aDate.SetYear((USHORT) nYear); + if ( nDayOfMonth > 28 ) + aDate.SetDay( Min( aDate.GetDaysInMonth(), nDayOfMonth ) ); + } + } + break; + case FILL_YEAR: + { + long nYear = aDate.GetYear(); + nYear += nInc; + if ( nYear < nMinYear ) + aDate = Date( 1,1, nMinYear ); + else if ( nYear > nMaxYear ) + aDate = Date( 31,12, nMaxYear ); + else + aDate.SetYear((USHORT) nYear); + } + break; + default: + { + // added to avoid warnings + } + } + + rVal = aDate - aNullDate; +} + +void ScTable::FillSeries( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ULONG nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd, + double nStepValue, double nMaxValue, USHORT nArgMinDigits, + BOOL bAttribs, ScProgress& rProgress ) +{ + // + // Richtung auswerten + // + + BOOL bVertical = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP); + BOOL bPositive = (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_RIGHT); + + ULONG nCol = 0; + ULONG nRow = 0; + ULONG& rInner = bVertical ? nRow : nCol; // Schleifenvariablen + ULONG& rOuter = bVertical ? nCol : nRow; + ULONG nOStart; + ULONG nOEnd; + ULONG nIStart; + ULONG nIEnd; + ULONG nISource; + + if (bVertical) + { + nFillCount += (nRow2 - nRow1); + if (nFillCount == 0) + return; + nOStart = nCol1; + nOEnd = nCol2; + if (bPositive) + { + nISource = nRow1; + nIStart = nRow1 + 1; + nIEnd = nRow1 + nFillCount; + } + else + { + nISource = nRow2; + nIStart = nRow2 - 1; + nIEnd = nRow2 - nFillCount; + } + } + else + { + nFillCount += (nCol2 - nCol1); + if (nFillCount == 0) + return; + nOStart = nRow1; + nOEnd = nRow2; + if (bPositive) + { + nISource = nCol1; + nIStart = nCol1 + 1; + nIEnd = nCol1 + nFillCount; + } + else + { + nISource = nCol2; + nIStart = nCol2 - 1; + nIEnd = nCol2 - nFillCount; + } + } + + ULONG nIMin = nIStart; + ULONG nIMax = nIEnd; + PutInOrder(nIMin,nIMax); + USHORT nDel = bAttribs ? IDF_AUTOFILL : (IDF_AUTOFILL & IDF_CONTENTS); + if (bVertical) + DeleteArea(nCol1, static_cast<SCROW>(nIMin), nCol2, static_cast<SCROW>(nIMax), nDel); + else + DeleteArea(static_cast<SCCOL>(nIMin), nRow1, static_cast<SCCOL>(nIMax), nRow2, nDel); + + ULONG nProgress = rProgress.GetState(); + + // + // ausfuehren + // + + ULONG nActFormCnt = 0; + for (rOuter = nOStart; rOuter <= nOEnd; rOuter++) + { + BOOL bFirst = TRUE; + rInner = nISource; + ScBaseCell* pSrcCell = aCol[nCol].GetCell(static_cast<SCROW>(nRow)); + + if (bVertical && bAttribs) + aCol[nCol].Resize( aCol[nCol].GetCellCount() + nFillCount ); + + if (bAttribs) + { + const ScPatternAttr* pSrcPattern = aCol[nCol].GetPattern(static_cast<SCROW>(nRow)); + if (bVertical) + aCol[nCol].SetPatternArea( static_cast<SCROW>(nIMin), + static_cast<SCROW>(nIMax), *pSrcPattern, TRUE ); + else + for (SCCOL nAtCol = static_cast<SCCOL>(nIMin); nAtCol <= sal::static_int_cast<SCCOL>(nIMax); nAtCol++) + aCol[nAtCol].SetPattern(static_cast<SCROW>(nRow), *pSrcPattern, TRUE); + } + + if (pSrcCell) + { + CellType eCellType = pSrcCell->GetCellType(); + + if (eFillCmd == FILL_SIMPLE) // kopieren + { + if (eCellType == CELLTYPE_FORMULA) + { + for (rInner = nIMin; rInner <= nIMax; rInner++) + { + ULONG nInd = nActFormCnt; + FillFormula(nInd, bFirst, (ScFormulaCell*)pSrcCell, + static_cast<SCCOL>(nCol), nRow, (rInner == nIEnd) ); + bFirst = FALSE; + rProgress.SetStateOnPercent( ++nProgress ); + } + } + else if (eCellType != CELLTYPE_NOTE) + { + for (rInner = nIMin; rInner <= nIMax; rInner++) + { + ScAddress aDestPos( static_cast<SCCOL>(nCol), static_cast<SCROW>(nRow), nTab ); + aCol[nCol].Insert( aDestPos.Row(), pSrcCell->CloneWithoutNote( *pDocument ) ); + } + nProgress += nIMax - nIMin + 1; + rProgress.SetStateOnPercent( nProgress ); + } + } + else if (eCellType == CELLTYPE_VALUE || eCellType == CELLTYPE_FORMULA) + { + double nStartVal; + if (eCellType == CELLTYPE_VALUE) + nStartVal = ((ScValueCell*)pSrcCell)->GetValue(); + else + nStartVal = ((ScFormulaCell*)pSrcCell)->GetValue(); + double nVal = nStartVal; + long nIndex = 0; + + BOOL bError = FALSE; + BOOL bOverflow = FALSE; + + USHORT nDayOfMonth = 0; + rInner = nIStart; + while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + { + if (!bError && !bOverflow) + { + switch (eFillCmd) + { + case FILL_LINEAR: + { + // #86365# use multiplication instead of repeated addition + // to avoid accumulating rounding errors + nVal = nStartVal; + double nAdd = nStepValue; + if ( !SubTotal::SafeMult( nAdd, (double) ++nIndex ) || + !SubTotal::SafePlus( nVal, nAdd ) ) + bError = TRUE; + } + break; + case FILL_GROWTH: + if (!SubTotal::SafeMult(nVal, nStepValue)) + bError = TRUE; + break; + case FILL_DATE: + if (fabs(nVal) > _D_MAX_LONG_) + bError = TRUE; + else + IncDate(nVal, nDayOfMonth, nStepValue, eFillDateCmd); + break; + default: + { + // added to avoid warnings + } + } + + if (nStepValue >= 0) + { + if (nVal > nMaxValue) // Zielwert erreicht? + { + nVal = nMaxValue; + bOverflow = TRUE; + } + } + else + { + if (nVal < nMaxValue) + { + nVal = nMaxValue; + bOverflow = TRUE; + } + } + } + + if (bError) + aCol[nCol].SetError(static_cast<SCROW>(nRow), errNoValue); + else if (!bOverflow) + aCol[nCol].SetValue(static_cast<SCROW>(nRow), nVal); + + if (rInner == nIEnd) break; + if (bPositive) ++rInner; else --rInner; + } + nProgress += nIMax - nIMin + 1; + rProgress.SetStateOnPercent( nProgress ); + } + else if (eCellType == CELLTYPE_STRING || eCellType == CELLTYPE_EDIT) + { + if ( nStepValue >= 0 ) + { + if ( nMaxValue >= (double)LONG_MAX ) + nMaxValue = (double)LONG_MAX - 1; + } + else + { + if ( nMaxValue <= (double)LONG_MIN ) + nMaxValue = (double)LONG_MIN + 1; + } + String aValue; + if (eCellType == CELLTYPE_STRING) + ((ScStringCell*)pSrcCell)->GetString( aValue ); + else + ((ScEditCell*)pSrcCell)->GetString( aValue ); + sal_Int32 nStringValue; + USHORT nMinDigits = nArgMinDigits; + short nHeadNoneTail = lcl_DecompValueString( aValue, nStringValue, &nMinDigits ); + if ( nHeadNoneTail ) + { + double nStartVal = (double)nStringValue; + double nVal = nStartVal; + long nIndex = 0; + BOOL bError = FALSE; + BOOL bOverflow = FALSE; + + BOOL bIsOrdinalSuffix = aValue.Equals( ScGlobal::GetOrdinalSuffix( + (sal_Int32)nStartVal)); + + rInner = nIStart; + while (true) // #i53728# with "for (;;)" old solaris/x86 compiler mis-optimizes + { + if (!bError && !bOverflow) + { + switch (eFillCmd) + { + case FILL_LINEAR: + { + // #86365# use multiplication instead of repeated addition + // to avoid accumulating rounding errors + nVal = nStartVal; + double nAdd = nStepValue; + if ( !SubTotal::SafeMult( nAdd, (double) ++nIndex ) || + !SubTotal::SafePlus( nVal, nAdd ) ) + bError = TRUE; + } + break; + case FILL_GROWTH: + if (!SubTotal::SafeMult(nVal, nStepValue)) + bError = TRUE; + break; + default: + { + // added to avoid warnings + } + } + + if (nStepValue >= 0) + { + if (nVal > nMaxValue) // Zielwert erreicht? + { + nVal = nMaxValue; + bOverflow = TRUE; + } + } + else + { + if (nVal < nMaxValue) + { + nVal = nMaxValue; + bOverflow = TRUE; + } + } + } + + if (bError) + aCol[nCol].SetError(static_cast<SCROW>(nRow), errNoValue); + else if (!bOverflow) + { + nStringValue = (sal_Int32)nVal; + String aStr; + if ( nHeadNoneTail < 0 ) + { + aCol[nCol].Insert( static_cast<SCROW>(nRow), + lcl_getSuffixCell( pDocument, + nStringValue, nMinDigits, aValue, + eCellType, bIsOrdinalSuffix )); + } + else + { + aStr = aValue; + aStr += lcl_ValueString( nStringValue, nMinDigits ); + ScStringCell* pCell = new ScStringCell( aStr ); + aCol[nCol].Insert( static_cast<SCROW>(nRow), pCell ); + } + } + + if (rInner == nIEnd) break; + if (bPositive) ++rInner; else --rInner; + } + } + nProgress += nIMax - nIMin + 1; + rProgress.SetStateOnPercent( nProgress ); + } + } + else + { + nProgress += nIMax - nIMin + 1; + rProgress.SetStateOnPercent( nProgress ); + } + ++nActFormCnt; + } +} + +void ScTable::Fill( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, + ULONG nFillCount, FillDir eFillDir, FillCmd eFillCmd, FillDateCmd eFillDateCmd, + double nStepValue, double nMaxValue) +{ + ULONG nProgCount; + if (eFillDir == FILL_TO_BOTTOM || eFillDir == FILL_TO_TOP) + nProgCount = nCol2 - nCol1 + 1; + else + nProgCount = nRow2 - nRow1 + 1; + nProgCount *= nFillCount; + ScProgress aProgress( pDocument->GetDocumentShell(), + ScGlobal::GetRscString(STR_FILL_SERIES_PROGRESS), nProgCount ); + + bSharedNameInserted = FALSE; + + if (eFillCmd == FILL_AUTO) + FillAuto(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir, aProgress); + else + FillSeries(nCol1, nRow1, nCol2, nRow2, nFillCount, eFillDir, + eFillCmd, eFillDateCmd, nStepValue, nMaxValue, 0, TRUE, aProgress); + + if (bSharedNameInserted) // Wurde Shared-Name eingefuegt? + pDocument->GetRangeName()->SetSharedMaxIndex( + pDocument->GetRangeName()->GetSharedMaxIndex()+1); // dann hochzaehlen +} + + +void ScTable::AutoFormatArea(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + const ScPatternAttr& rAttr, USHORT nFormatNo) +{ + ScAutoFormat* pAutoFormat = ScGlobal::GetAutoFormat(); + if (pAutoFormat) + { + ScAutoFormatData* pData = (*pAutoFormat)[nFormatNo]; + if (pData) + { +// ScPatternAttr aPattern(pDocument->GetPool()); +// pData->FillToItemSet(nIndex, aPattern.GetItemSet(), *pDocument); + ApplyPatternArea(nStartCol, nStartRow, nEndCol, nEndRow, rAttr); + } + } +} + +void ScTable::AutoFormat( SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, + USHORT nFormatNo ) +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + ScAutoFormat* pAutoFormat = ScGlobal::GetAutoFormat(); + if (pAutoFormat) + { + ScAutoFormatData* pData = (*pAutoFormat)[nFormatNo]; + if (pData) + { + ScPatternAttr* pPatternAttrs[16]; + for (sal_uInt8 i = 0; i < 16; ++i) + { + pPatternAttrs[i] = new ScPatternAttr(pDocument->GetPool()); + pData->FillToItemSet(i, pPatternAttrs[i]->GetItemSet(), *pDocument); + } + + SCCOL nCol = nStartCol; + SCROW nRow = nStartRow; + USHORT nIndex = 0; + // Linke obere Ecke + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + // Linke Spalte + if (pData->IsEqualData(4, 8)) + AutoFormatArea(nStartCol, nStartRow + 1, nStartCol, nEndRow - 1, *pPatternAttrs[4], nFormatNo); + else + { + nIndex = 4; + for (nRow = nStartRow + 1; nRow < nEndRow; nRow++) + { + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + if (nIndex == 4) + nIndex = 8; + else + nIndex = 4; + } + } + // Linke untere Ecke + nRow = nEndRow; + nIndex = 12; + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + // Rechte obere Ecke + nCol = nEndCol; + nRow = nStartRow; + nIndex = 3; + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + // Rechte Spalte + if (pData->IsEqualData(7, 11)) + AutoFormatArea(nEndCol, nStartRow + 1, nEndCol, nEndRow - 1, *pPatternAttrs[7], nFormatNo); + else + { + nIndex = 7; + for (nRow = nStartRow + 1; nRow < nEndRow; nRow++) + { + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + if (nIndex == 7) + nIndex = 11; + else + nIndex = 7; + } + } + // Rechte untere Ecke + nRow = nEndRow; + nIndex = 15; + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + nRow = nStartRow; + nIndex = 1; + for (nCol = nStartCol + 1; nCol < nEndCol; nCol++) + { + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + if (nIndex == 1) + nIndex = 2; + else + nIndex = 1; + } + // Untere Zeile + nRow = nEndRow; + nIndex = 13; + for (nCol = nStartCol + 1; nCol < nEndCol; nCol++) + { + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + if (nIndex == 13) + nIndex = 14; + else + nIndex = 13; + } + // Boddy + if ((pData->IsEqualData(5, 6)) && (pData->IsEqualData(9, 10)) && (pData->IsEqualData(5, 9))) + AutoFormatArea(nStartCol + 1, nStartRow + 1, nEndCol-1, nEndRow - 1, *pPatternAttrs[5], nFormatNo); + else + { + if ((pData->IsEqualData(5, 9)) && (pData->IsEqualData(6, 10))) + { + nIndex = 5; + for (nCol = nStartCol + 1; nCol < nEndCol; nCol++) + { + AutoFormatArea(nCol, nStartRow + 1, nCol, nEndRow - 1, *pPatternAttrs[nIndex], nFormatNo); + if (nIndex == 5) + nIndex = 6; + else + nIndex = 5; + } + } + else + { + nIndex = 5; + for (nCol = nStartCol + 1; nCol < nEndCol; nCol++) + { + for (nRow = nStartRow + 1; nRow < nEndRow; nRow++) + { + AutoFormatArea(nCol, nRow, nCol, nRow, *pPatternAttrs[nIndex], nFormatNo); + if ((nIndex == 5) || (nIndex == 9)) + { + if (nIndex == 5) + nIndex = 9; + else + nIndex = 5; + } + else + { + if (nIndex == 6) + nIndex = 10; + else + nIndex = 6; + } + } // for nRow + if ((nIndex == 5) || (nIndex == 9)) + nIndex = 6; + else + nIndex = 5; + } // for nCol + } // if not equal Column + } // if not all equal + + for (sal_uInt8 j = 0; j < 16; ++j) + delete pPatternAttrs[j]; + } // if AutoFormatData != NULL + } // if AutoFormat != NULL + } // if ValidColRow +} + +void ScTable::GetAutoFormatAttr(SCCOL nCol, SCROW nRow, USHORT nIndex, ScAutoFormatData& rData) +{ + UINT32 nFormatIndex = GetNumberFormat( nCol, nRow ); + ScNumFormatAbbrev aNumFormat( nFormatIndex, *pDocument->GetFormatTable() ); + rData.GetFromItemSet( nIndex, GetPattern( nCol, nRow )->GetItemSet(), aNumFormat ); +} + +#define LF_LEFT 1 +#define LF_TOP 2 +#define LF_RIGHT 4 +#define LF_BOTTOM 8 +#define LF_ALL (LF_LEFT | LF_TOP | LF_RIGHT | LF_BOTTOM) + +void ScTable::GetAutoFormatFrame(SCCOL nCol, SCROW nRow, USHORT nFlags, USHORT nIndex, ScAutoFormatData& rData) +{ + const SvxBoxItem* pTheBox = (SvxBoxItem*)GetAttr(nCol, nRow, ATTR_BORDER); + const SvxBoxItem* pLeftBox = (SvxBoxItem*)GetAttr(nCol - 1, nRow, ATTR_BORDER); + const SvxBoxItem* pTopBox = (SvxBoxItem*)GetAttr(nCol, nRow - 1, ATTR_BORDER); + const SvxBoxItem* pRightBox = (SvxBoxItem*)GetAttr(nCol + 1, nRow, ATTR_BORDER); + const SvxBoxItem* pBottomBox = (SvxBoxItem*)GetAttr(nCol, nRow + 1, ATTR_BORDER); + + SvxBoxItem aBox( ATTR_BORDER ); + if (nFlags & LF_LEFT) + { + if (pLeftBox) + { + if (ScHasPriority(pTheBox->GetLeft(), pLeftBox->GetRight())) + aBox.SetLine(pTheBox->GetLeft(), BOX_LINE_LEFT); + else + aBox.SetLine(pLeftBox->GetRight(), BOX_LINE_LEFT); + } + else + aBox.SetLine(pTheBox->GetLeft(), BOX_LINE_LEFT); + } + if (nFlags & LF_TOP) + { + if (pTopBox) + { + if (ScHasPriority(pTheBox->GetTop(), pTopBox->GetBottom())) + aBox.SetLine(pTheBox->GetTop(), BOX_LINE_TOP); + else + aBox.SetLine(pTopBox->GetBottom(), BOX_LINE_TOP); + } + else + aBox.SetLine(pTheBox->GetTop(), BOX_LINE_TOP); + } + if (nFlags & LF_RIGHT) + { + if (pRightBox) + { + if (ScHasPriority(pTheBox->GetRight(), pRightBox->GetLeft())) + aBox.SetLine(pTheBox->GetRight(), BOX_LINE_RIGHT); + else + aBox.SetLine(pRightBox->GetLeft(), BOX_LINE_RIGHT); + } + else + aBox.SetLine(pTheBox->GetRight(), BOX_LINE_RIGHT); + } + if (nFlags & LF_BOTTOM) + { + if (pBottomBox) + { + if (ScHasPriority(pTheBox->GetBottom(), pBottomBox->GetTop())) + aBox.SetLine(pTheBox->GetBottom(), BOX_LINE_BOTTOM); + else + aBox.SetLine(pBottomBox->GetTop(), BOX_LINE_BOTTOM); + } + else + aBox.SetLine(pTheBox->GetBottom(), BOX_LINE_BOTTOM); + } + rData.PutItem( nIndex, aBox ); +} + +void ScTable::GetAutoFormatData(SCCOL nStartCol, SCROW nStartRow, SCCOL nEndCol, SCROW nEndRow, ScAutoFormatData& rData) +{ + if (ValidColRow(nStartCol, nStartRow) && ValidColRow(nEndCol, nEndRow)) + { + if ((nEndCol - nStartCol >= 3) && (nEndRow - nStartRow >= 3)) + { + // Linke obere Ecke + GetAutoFormatAttr(nStartCol, nStartRow, 0, rData); + GetAutoFormatFrame(nStartCol, nStartRow, LF_ALL, 0, rData); + // Linke Spalte + GetAutoFormatAttr(nStartCol, nStartRow + 1, 4, rData); + GetAutoFormatAttr(nStartCol, nStartRow + 2, 8, rData); + GetAutoFormatFrame(nStartCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 4, rData); + if (nEndRow - nStartRow >= 4) + GetAutoFormatFrame(nStartCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 8, rData); + else + rData.CopyItem( 8, 4, ATTR_BORDER ); + // Linke untere Ecke + GetAutoFormatAttr(nStartCol, nEndRow, 12, rData); + GetAutoFormatFrame(nStartCol, nEndRow, LF_ALL, 12, rData); + // Rechte obere Ecke + GetAutoFormatAttr(nEndCol, nStartRow, 3, rData); + GetAutoFormatFrame(nEndCol, nStartRow, LF_ALL, 3, rData); + // Rechte Spalte + GetAutoFormatAttr(nEndCol, nStartRow + 1, 7, rData); + GetAutoFormatAttr(nEndCol, nStartRow + 2, 11, rData); + GetAutoFormatFrame(nEndCol, nStartRow + 1, LF_LEFT | LF_RIGHT | LF_BOTTOM, 7, rData); + if (nEndRow - nStartRow >= 4) + GetAutoFormatFrame(nEndCol, nStartRow + 2, LF_LEFT | LF_RIGHT | LF_BOTTOM, 11, rData); + else + rData.CopyItem( 11, 7, ATTR_BORDER ); + // Rechte untere Ecke + GetAutoFormatAttr(nEndCol, nEndRow, 15, rData); + GetAutoFormatFrame(nEndCol, nEndRow, LF_ALL, 15, rData); + // Ober Zeile + GetAutoFormatAttr(nStartCol + 1, nStartRow, 1, rData); + GetAutoFormatAttr(nStartCol + 2, nStartRow, 2, rData); + GetAutoFormatFrame(nStartCol + 1, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 1, rData); + if (nEndCol - nStartCol >= 4) + GetAutoFormatFrame(nStartCol + 2, nStartRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 2, rData); + else + rData.CopyItem( 2, 1, ATTR_BORDER ); + // Untere Zeile + GetAutoFormatAttr(nStartCol + 1, nEndRow, 13, rData); + GetAutoFormatAttr(nStartCol + 2, nEndRow, 14, rData); + GetAutoFormatFrame(nStartCol + 1, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 13, rData); + if (nEndCol - nStartCol >= 4) + GetAutoFormatFrame(nStartCol + 2, nEndRow, LF_TOP | LF_BOTTOM | LF_RIGHT, 14, rData); + else + rData.CopyItem( 14, 13, ATTR_BORDER ); + // Body + GetAutoFormatAttr(nStartCol + 1, nStartRow + 1, 5, rData); + GetAutoFormatAttr(nStartCol + 2, nStartRow + 1, 6, rData); + GetAutoFormatAttr(nStartCol + 1, nStartRow + 2, 9, rData); + GetAutoFormatAttr(nStartCol + 2, nStartRow + 2, 10, rData); + GetAutoFormatFrame(nStartCol + 1, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 5, rData); + if ((nEndCol - nStartCol >= 4) && (nEndRow - nStartRow >= 4)) + { + GetAutoFormatFrame(nStartCol + 2, nStartRow + 1, LF_RIGHT | LF_BOTTOM, 6, rData); + GetAutoFormatFrame(nStartCol + 1, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 9, rData); + GetAutoFormatFrame(nStartCol + 2, nStartRow + 2, LF_RIGHT | LF_BOTTOM, 10, rData); + } + else + { + rData.CopyItem( 6, 5, ATTR_BORDER ); + rData.CopyItem( 9, 5, ATTR_BORDER ); + rData.CopyItem( 10, 5, ATTR_BORDER ); + } + } + } +} + +void ScTable::SetError( SCCOL nCol, SCROW nRow, USHORT nError) +{ + if (ValidColRow(nCol, nRow)) + aCol[nCol].SetError( nRow, nError ); +} + +void ScTable::UpdateInsertTabAbs(SCTAB nTable) +{ + for (SCCOL i=0; i <= MAXCOL; i++) + aCol[i].UpdateInsertTabAbs(nTable); +} + +//UNUSED2008-05 USHORT ScTable::GetErrorData( SCCOL nCol, SCROW nRow ) const +//UNUSED2008-05 { +//UNUSED2008-05 if (ValidColRow(nCol,nRow)) +//UNUSED2008-05 return aCol[nCol].GetErrorData( nRow ); +//UNUSED2008-05 else +//UNUSED2008-05 return 0; +//UNUSED2008-05 } + +BOOL ScTable::GetNextSpellingCell(SCCOL& rCol, SCROW& rRow, BOOL bInSel, + const ScMarkData& rMark) const +{ + if (rRow == MAXROW+2) // Tabellenende + { + rRow = 0; + rCol = 0; + } + else + { + rRow++; + if (rRow == MAXROW+1) + { + rCol++; + rRow = 0; + } + } + if (rCol == MAXCOL+1) + return TRUE; + else + { + BOOL bStop = FALSE; + while (!bStop) + { + if (ValidCol(rCol)) + { + bStop = aCol[rCol].GetNextSpellingCell(rRow, bInSel, rMark); + if (bStop) + return TRUE; + else /*if (rRow == MAXROW+1) */ + { + rCol++; + rRow = 0; + } + } + else + return TRUE; + } + } + return FALSE; +} + +void ScTable::RemoveAutoSpellObj() +{ + for (SCCOL i=0; i <= MAXCOL; i++) + aCol[i].RemoveAutoSpellObj(); +} + +BOOL ScTable::TestTabRefAbs(SCTAB nTable) +{ + BOOL bRet = FALSE; + for (SCCOL i=0; i <= MAXCOL; i++) + if (aCol[i].TestTabRefAbs(nTable)) + bRet = TRUE; + return bRet; +} + +void ScTable::CompileDBFormula() +{ + for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileDBFormula(); +} + +void ScTable::CompileDBFormula( BOOL bCreateFormulaString ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileDBFormula( bCreateFormulaString ); +} + +void ScTable::CompileNameFormula( BOOL bCreateFormulaString ) +{ + for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileNameFormula( bCreateFormulaString ); +} + +void ScTable::CompileColRowNameFormula() +{ + for (SCCOL i=0; i<=MAXCOL; i++) aCol[i].CompileColRowNameFormula(); +} + + + + + + diff --git a/sc/source/core/data/table5.cxx b/sc/source/core/data/table5.cxx new file mode 100644 index 000000000000..4a0cd9e135d5 --- /dev/null +++ b/sc/source/core/data/table5.cxx @@ -0,0 +1,449 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table5.cxx,v $ + * $Revision: 1.14 $ + * + * 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 "scitems.hxx" +#include "collect.hxx" +#include "attrib.hxx" +#include "patattr.hxx" +#include "docpool.hxx" +#include "cell.hxx" +#include "table.hxx" +#include "column.hxx" +#include "document.hxx" +#include "drwlayer.hxx" +#include "olinetab.hxx" +#include "userlist.hxx" +#include "stlsheet.hxx" +#include "global.hxx" +#include "rechead.hxx" +#include "stlpool.hxx" +#include "stlsheet.hxx" +#include "brdcst.hxx" +#include "tabprotection.hxx" +#include "globstr.hrc" + +using ::com::sun::star::uno::Sequence; + +// STATIC DATA ----------------------------------------------------------- + +#define GET_SCALEVALUE(set,id) ((const SfxUInt16Item&)(set.Get( id ))).GetValue() + + +void ScTable::UpdatePageBreaks( const ScRange* pUserArea ) +{ + if ( pDocument->IsImportingXML() ) + return; + if ( !pUserArea && !bPageSizeValid ) + return; + + SfxStyleSheetBase* pStyle = pDocument->GetStyleSheetPool()-> + Find( aPageStyle, SFX_STYLE_FAMILY_PAGE ); + if ( !pStyle ) + { + DBG_ERROR("UpdatePageBreaks: Style nicht gefunden"); + return; + } + SfxItemSet* pStyleSet = &pStyle->GetItemSet(); + const SfxPoolItem* pItem; + + SCCOL nX; + SCROW nY; + SCCOL nStartCol = 0; + SCROW nStartRow = 0; + SCCOL nEndCol = MAXCOL; + SCROW nEndRow = MAXROW; + if (pUserArea) + { + nStartCol = pUserArea->aStart.Col(); + nStartRow = pUserArea->aStart.Row(); + nEndCol = pUserArea->aEnd.Col(); + nEndRow = pUserArea->aEnd.Row(); + } + else + { + USHORT nAreaCount = GetPrintRangeCount(); + if ( nAreaCount > 1 ) + { + // bei mehreren Bereichen nichts anzeigen: + + for (nX=0; nX<MAXCOL; nX++) + pColFlags[nX] &= ~CR_PAGEBREAK; + pRowFlags->AndValue( 0, MAXROW-1, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + + return; + } + else if ( nAreaCount == 1 ) + { + const ScRange* pArea = GetPrintRange( 0 ); + if (pArea) + { + nStartCol = pArea->aStart.Col(); + nStartRow = pArea->aStart.Row(); + nEndCol = pArea->aEnd.Col(); + nEndRow = pArea->aEnd.Row(); + } + } // sonst alles + } + + // bSkipBreaks holen: + + BOOL bSkipBreaks = FALSE; + + if ( pStyleSet->GetItemState( ATTR_PAGE_SCALETOPAGES, FALSE, &pItem ) == SFX_ITEM_SET ) + { + DBG_ASSERT( pItem->ISA(SfxUInt16Item), "falsches Item" ); + bSkipBreaks = ( ((const SfxUInt16Item*)pItem)->GetValue() > 0 ); + } + + //-------------------------------------------------------------------------- + + long nPageSizeX = aPageSizeTwips.Width(); + long nPageSizeY = aPageSizeTwips.Height(); + + // Anfang: Breaks loeschen + + for (nX=0; nX<nStartCol; nX++) + pColFlags[nX] &= ~CR_PAGEBREAK; + pRowFlags->AndValue( 0, nStartRow-1, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + + if (nStartCol > 0) + pColFlags[nStartCol] |= CR_PAGEBREAK; //! AREABREAK + if (nStartRow > 0) + pRowFlags->OrValue( nStartRow, CR_PAGEBREAK); //! AREABREAK + + // Mittelteil: Breaks verteilen + + BOOL bRepeatCol = ( nRepeatStartX != SCCOL_REPEAT_NONE ); + BOOL bColFound = FALSE; + long nSizeX = 0; + for (nX=nStartCol; nX<=nEndCol; nX++) + { + BOOL bStartOfPage = FALSE; + long nThisX = ( pColFlags[nX] & CR_HIDDEN ) ? 0 : pColWidth[nX]; + if ( (nSizeX+nThisX > nPageSizeX) || ((pColFlags[nX] & CR_MANUALBREAK) && !bSkipBreaks) ) + { + pColFlags[nX] |= CR_PAGEBREAK; + nSizeX = 0; + bStartOfPage = TRUE; + } + else if (nX != nStartCol) + pColFlags[nX] &= ~CR_PAGEBREAK; + else + bStartOfPage = TRUE; + + if ( bStartOfPage && bRepeatCol && nX>nRepeatStartX && !bColFound ) + { + // subtract size of repeat columns from page size + for (SCCOL i=nRepeatStartX; i<=nRepeatEndX; i++) + nPageSizeX -= ( pColFlags[i] & CR_HIDDEN ) ? 0 : pColWidth[i]; + while (nX<=nRepeatEndX) + pColFlags[++nX] &= ~CR_PAGEBREAK; + bColFound = TRUE; + } + + nSizeX += nThisX; + } + + // Remove all page breaks in range. + pRowFlags->AndValue( nStartRow+1, nEndRow, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + // And set new page breaks. + BOOL bRepeatRow = ( nRepeatStartY != SCROW_REPEAT_NONE ); + BOOL bRowFound = FALSE; + long nSizeY = 0; + ScCompressedArrayIterator< SCROW, BYTE> aFlagsIter( *pRowFlags, nStartRow, nEndRow); + ScCompressedArrayIterator< SCROW, USHORT> aHeightIter( *pRowHeight, nStartRow, nEndRow); + for ( ; aFlagsIter; ++aFlagsIter, ++aHeightIter) + { + nY = aFlagsIter.GetPos(); + BOOL bStartOfPage = FALSE; + BYTE nFlags = *aFlagsIter; + long nThisY = (nFlags & CR_HIDDEN) ? 0 : *aHeightIter; + if ( (nSizeY+nThisY > nPageSizeY) || ((nFlags & CR_MANUALBREAK) && !bSkipBreaks) ) + { + pRowFlags->SetValue( nY, nFlags | CR_PAGEBREAK); + aFlagsIter.Resync( nY); + nSizeY = 0; + bStartOfPage = TRUE; + } + else if (nY != nStartRow) + ; // page break already removed + else + bStartOfPage = TRUE; + + if ( bStartOfPage && bRepeatRow && nY>nRepeatStartY && !bRowFound ) + { + // subtract size of repeat rows from page size + unsigned long nHeights = pRowFlags->SumCoupledArrayForCondition( + nRepeatStartY, nRepeatEndY, CR_HIDDEN, 0, *pRowHeight); +#ifdef DBG_UTIL + if (nHeights == ::std::numeric_limits<unsigned long>::max()) + DBG_ERRORFILE("ScTable::UpdatePageBreaks: row heights overflow"); +#endif + nPageSizeY -= nHeights; + if (nY <= nRepeatEndY) + { + pRowFlags->AndValue( nY, nRepeatEndY, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + nY = nRepeatEndY + 1; + aFlagsIter.Resync( nY); + aHeightIter.Resync( nY); + } + bRowFound = TRUE; + } + + nSizeY += nThisY; + } + + // Ende: Breaks loeschen + + if (nEndCol < MAXCOL) + { + pColFlags[nEndCol+1] |= CR_PAGEBREAK; //! AREABREAK + for (nX=nEndCol+2; nX<=MAXCOL; nX++) + pColFlags[nX] &= ~CR_PAGEBREAK; + } + if (nEndRow < MAXROW) + { + pRowFlags->OrValue( nEndRow+1, CR_PAGEBREAK); //! AREABREAK + if (nEndRow+2 <= MAXROW) + pRowFlags->AndValue( nEndRow+2, MAXROW, sal::static_int_cast<BYTE>(~CR_PAGEBREAK) ); + } +} + +void ScTable::RemoveManualBreaks() +{ + if (pColFlags) + for (SCCOL nCol = 0; nCol <= MAXCOL; nCol++) + pColFlags[nCol] &= ~CR_MANUALBREAK; + + if (pRowFlags) + pRowFlags->AndValue( 0, MAXROW, sal::static_int_cast<BYTE>(~CR_MANUALBREAK) ); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +BOOL ScTable::HasManualBreaks() const +{ + if (pColFlags) + for (SCCOL nCol = 0; nCol <= MAXCOL; nCol++) + if ( pColFlags[nCol] & CR_MANUALBREAK ) + return TRUE; + + if (pRowFlags) + if (ValidRow( pRowFlags->GetLastAnyBitAccess( 0, CR_MANUALBREAK))) + return TRUE; + + return FALSE; +} + +void ScTable::SetPageSize( const Size& rSize ) +{ + if ( rSize.Width() != 0 && rSize.Height() != 0 ) + { + bPageSizeValid = TRUE; + aPageSizeTwips = rSize; + } + else + bPageSizeValid = FALSE; +} + +BOOL ScTable::IsProtected() const +{ + return pTabProtection.get() && pTabProtection->isProtected(); +} + +void ScTable::SetProtection(const ScTableProtection* pProtect) +{ + if (pProtect) + pTabProtection.reset(new ScTableProtection(*pProtect)); + else + pTabProtection.reset(NULL); + + if (IsStreamValid()) + SetStreamValid(FALSE); +} + +ScTableProtection* ScTable::GetProtection() +{ + return pTabProtection.get(); +} + +Size ScTable::GetPageSize() const +{ + if ( bPageSizeValid ) + return aPageSizeTwips; + else + return Size(); // leer +} + +void ScTable::SetRepeatArea( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow ) +{ + nRepeatStartX = nStartCol; + nRepeatEndX = nEndCol; + nRepeatStartY = nStartRow; + nRepeatEndY = nEndRow; +} + +void ScTable::StartListening( const ScAddress& rAddress, SvtListener* pListener ) +{ + aCol[rAddress.Col()].StartListening( *pListener, rAddress.Row() ); +} + +void ScTable::EndListening( const ScAddress& rAddress, SvtListener* pListener ) +{ + aCol[rAddress.Col()].EndListening( *pListener, rAddress.Row() ); +} + +void ScTable::SetPageStyle( const String& rName ) +{ + if ( aPageStyle != rName ) + { + String aStrNew = rName; + SfxStyleSheetBasePool* pStylePool = pDocument->GetStyleSheetPool(); + SfxStyleSheetBase* pNewStyle = pStylePool->Find( aStrNew, SFX_STYLE_FAMILY_PAGE ); + + if ( !pNewStyle ) + { + aStrNew = ScGlobal::GetRscString(STR_STYLENAME_STANDARD); + pNewStyle = pStylePool->Find( aStrNew, SFX_STYLE_FAMILY_PAGE ); + } + + if ( aPageStyle != aStrNew ) + { + SfxStyleSheetBase* pOldStyle = pStylePool->Find( aPageStyle, SFX_STYLE_FAMILY_PAGE ); + + if ( pOldStyle && pNewStyle ) + { + SfxItemSet& rOldSet = pOldStyle->GetItemSet(); + SfxItemSet& rNewSet = pNewStyle->GetItemSet(); + const USHORT nOldScale = GET_SCALEVALUE(rOldSet,ATTR_PAGE_SCALE); + const USHORT nOldScaleToPages = GET_SCALEVALUE(rOldSet,ATTR_PAGE_SCALETOPAGES); + const USHORT nNewScale = GET_SCALEVALUE(rNewSet,ATTR_PAGE_SCALE); + const USHORT nNewScaleToPages = GET_SCALEVALUE(rNewSet,ATTR_PAGE_SCALETOPAGES); + + if ( (nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages) ) + InvalidateTextWidth(NULL, NULL, FALSE, FALSE); + } + + if ( pNewStyle ) // auch ohne den alten (fuer UpdateStdNames) + aPageStyle = aStrNew; + + if (IsStreamValid()) + SetStreamValid(FALSE); + } + } +} + +void ScTable::PageStyleModified( const String& rNewName ) +{ + aPageStyle = rNewName; + InvalidateTextWidth(NULL, NULL, FALSE, FALSE); // don't know what was in the style before +} + +void ScTable::InvalidateTextWidth( const ScAddress* pAdrFrom, const ScAddress* pAdrTo, + BOOL bNumFormatChanged, BOOL bBroadcast ) +{ + if ( pAdrFrom && !pAdrTo ) + { + ScBaseCell* pCell = aCol[pAdrFrom->Col()].GetCell( pAdrFrom->Row() ); + if ( pCell ) + { + pCell->SetTextWidth( TEXTWIDTH_DIRTY ); + if ( bNumFormatChanged ) + pCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); + if ( bBroadcast ) + { // nur bei CalcAsShown + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + pDocument->Broadcast( SC_HINT_DATACHANGED, + ScAddress( pAdrFrom->Col(), pAdrFrom->Row(), nTab ), + pCell ); + break; + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->SetDirty(); + break; + default: + { + // added to avoid warnings + } + } + } + } + } + else + { + const SCCOL nColStart = pAdrFrom ? pAdrFrom->Col() : 0; + const SCROW nRowStart = pAdrFrom ? pAdrFrom->Row() : 0; + const SCCOL nColEnd = pAdrTo ? pAdrTo->Col() : MAXCOL; + const SCROW nRowEnd = pAdrTo ? pAdrTo->Row() : MAXROW; + + for ( SCCOL nCol=nColStart; nCol<=nColEnd; nCol++ ) + { + ScColumnIterator aIter( &aCol[nCol], nRowStart, nRowEnd ); + ScBaseCell* pCell = NULL; + SCROW nRow = nRowStart; + + while ( aIter.Next( nRow, pCell ) ) + { + pCell->SetTextWidth( TEXTWIDTH_DIRTY ); + if ( bNumFormatChanged ) + pCell->SetScriptType( SC_SCRIPTTYPE_UNKNOWN ); + if ( bBroadcast ) + { // nur bei CalcAsShown + switch ( pCell->GetCellType() ) + { + case CELLTYPE_VALUE : + pDocument->Broadcast( SC_HINT_DATACHANGED, + ScAddress( nCol, nRow, nTab ), pCell ); + break; + case CELLTYPE_FORMULA : + ((ScFormulaCell*)pCell)->SetDirty(); + break; + default: + { + // added to avoid warnings + } + } + } + } + } + } +} + + + + + diff --git a/sc/source/core/data/table6.cxx b/sc/source/core/data/table6.cxx new file mode 100644 index 000000000000..09362b9996ed --- /dev/null +++ b/sc/source/core/data/table6.cxx @@ -0,0 +1,693 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: table6.cxx,v $ + * $Revision: 1.18.128.3 $ + * + * 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 <com/sun/star/i18n/TransliterationModules.hpp> + +#include <unotools/textsearch.hxx> +#include <svx/srchitem.hxx> +#include <svx/editobj.hxx> + +#include "table.hxx" +#include "collect.hxx" +#include "cell.hxx" +#include "document.hxx" +#include "stlpool.hxx" +#include "markdata.hxx" +#include "editutil.hxx" +#include "detfunc.hxx" +#include "postit.hxx" + +//-------------------------------------------------------------------------- + + +BOOL lcl_GetTextWithBreaks( const ScEditCell& rCell, ScDocument* pDoc, String& rVal ) +{ + // TRUE = more than 1 paragraph + + const EditTextObject* pData = NULL; + rCell.GetData( pData ); + EditEngine& rEngine = pDoc->GetEditEngine(); + rEngine.SetText( *pData ); + rVal = rEngine.GetText( LINEEND_LF ); + return ( rEngine.GetParagraphCount() > 1 ); +} + +BOOL ScTable::SearchCell(const SvxSearchItem& rSearchItem, SCCOL nCol, SCROW nRow, + const ScMarkData& rMark, String& rUndoStr, ScDocument* pUndoDoc) +{ + BOOL bFound = FALSE; + BOOL bDoSearch = TRUE; + BOOL bDoBack = rSearchItem.GetBackward(); + + String aString; + ScBaseCell* pCell; + if (rSearchItem.GetSelection()) + bDoSearch = rMark.IsCellMarked(nCol, nRow); + if ( bDoSearch && ((pCell = aCol[nCol].GetCell( nRow )) != NULL) ) + { + BOOL bMultiLine = FALSE; + CellType eCellType = pCell->GetCellType(); + switch (rSearchItem.GetCellType()) + { + case SVX_SEARCHIN_FORMULA: + { + if ( eCellType == CELLTYPE_FORMULA ) + ((ScFormulaCell*)pCell)->GetFormula( aString, + formula::FormulaGrammar::GRAM_NATIVE_UI); + else if ( eCellType == CELLTYPE_EDIT ) + bMultiLine = lcl_GetTextWithBreaks( + *(const ScEditCell*)pCell, pDocument, aString ); + else + aCol[nCol].GetInputString( nRow, aString ); + } + break; + case SVX_SEARCHIN_VALUE: + if ( eCellType == CELLTYPE_EDIT ) + bMultiLine = lcl_GetTextWithBreaks( + *(const ScEditCell*)pCell, pDocument, aString ); + else + aCol[nCol].GetInputString( nRow, aString ); + break; + case SVX_SEARCHIN_NOTE: + { + if(const ScPostIt* pNote = pCell->GetNote()) + { + aString = pNote->GetText(); + bMultiLine = pNote->HasMultiLineText(); + } + } + break; + default: + break; + } + xub_StrLen nStart = 0; + xub_StrLen nEnd = aString.Len(); + ::com::sun::star::util::SearchResult aSearchResult; + if (pSearchText) + { + if ( bDoBack ) + { + xub_StrLen nTemp=nStart; nStart=nEnd; nEnd=nTemp; + bFound = (BOOL)(pSearchText->SearchBkwrd(aString, &nStart, &nEnd, &aSearchResult)); + // change results to definition before 614: + --nEnd; + } + else + { + bFound = (BOOL)(pSearchText->SearchFrwrd(aString, &nStart, &nEnd, &aSearchResult)); + // change results to definition before 614: + --nEnd; + } + + if (bFound && rSearchItem.GetWordOnly()) + bFound = (nStart == 0 && nEnd == aString.Len() - 1); + } + else + { + DBG_ERROR("pSearchText == NULL"); + return bFound; + } + + BYTE cMatrixFlag = MM_NONE; + if ( bFound && + ( (rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE) + ||(rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL) ) && + // #60558# Matrix nicht zerreissen, nur Matrixformel ersetzen + !( (eCellType == CELLTYPE_FORMULA && + ((cMatrixFlag = ((ScFormulaCell*)pCell)->GetMatrixFlag()) == MM_REFERENCE)) + // kein UndoDoc => Matrix nicht wiederherstellbar => nicht ersetzen + || (cMatrixFlag != MM_NONE && !pUndoDoc) ) + ) + { + if ( cMatrixFlag == MM_NONE && rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE ) + rUndoStr = aString; + else if (pUndoDoc) + { + ScAddress aAdr( nCol, nRow, nTab ); + ScBaseCell* pUndoCell = pCell->CloneWithoutNote( *pUndoDoc ); + pUndoDoc->PutCell( aAdr, pUndoCell); + } + BOOL bRepeat = !rSearchItem.GetWordOnly(); + do + { + // wenn der gefundene Text leer ist, nicht weitersuchen, + // sonst wuerde man nie mehr aufhoeren (#35410#) + if ( nEnd < nStart || nEnd == STRING_MAXLEN ) + bRepeat = FALSE; + + String sReplStr = rSearchItem.GetReplaceString(); + if (rSearchItem.GetRegExp()) + { + String sFndStr = aString.Copy(nStart, nEnd-nStart+1); + pSearchText->ReplaceBackReferences( sReplStr, aString, aSearchResult ); + aString.Erase(nStart, nEnd-nStart+1); + aString.Insert(sReplStr, nStart); + } + else + { + aString.Erase(nStart, nEnd - nStart + 1); + aString.Insert(rSearchItem.GetReplaceString(), nStart); + } + + // Indizes anpassen + if (bDoBack) + { + nEnd = nStart; + nStart = 0; + } + else + { + nStart = sal::static_int_cast<xub_StrLen>( nStart + sReplStr.Len() ); + nEnd = aString.Len(); + } + + // weitersuchen ? + if (bRepeat) + { + if ( rSearchItem.GetCommand() != SVX_SEARCHCMD_REPLACE_ALL || nStart >= nEnd ) + bRepeat = FALSE; + else if (bDoBack) + { + xub_StrLen nTemp=nStart; nStart=nEnd; nEnd=nTemp; + bRepeat = ((BOOL)(pSearchText->SearchBkwrd(aString, &nStart, &nEnd))); + // change results to definition before 614: + --nEnd; + } + else + { + bRepeat = ((BOOL)(pSearchText->SearchFrwrd(aString, &nStart, &nEnd))); + // change results to definition before 614: + --nEnd; + } + } + } + while (bRepeat); + if (rSearchItem.GetCellType() == SVX_SEARCHIN_NOTE) + { + // NB: rich text format is lost. + // This is also true of Cells. + if( ScPostIt* pNote = pCell->GetNote() ) + pNote->SetText( ScAddress( nCol, nRow, nTab ), aString ); + } + else if ( cMatrixFlag != MM_NONE ) + { // #60558# Matrix nicht zerreissen + if ( aString.Len() > 2 ) + { // {} raus, erst hier damit auch "{=" durch "{=..." ersetzt werden kann + if ( aString.GetChar( aString.Len()-1 ) == '}' ) + aString.Erase( aString.Len()-1, 1 ); + if ( aString.GetChar(0) == '{' ) + aString.Erase( 0, 1 ); + } + ScAddress aAdr( nCol, nRow, nTab ); + ScFormulaCell* pFCell = new ScFormulaCell( pDocument, aAdr, + aString,formula::FormulaGrammar::GRAM_NATIVE_UI, cMatrixFlag ); + SCCOL nMatCols; + SCROW nMatRows; + ((ScFormulaCell*)pCell)->GetMatColsRows( nMatCols, nMatRows ); + pFCell->SetMatColsRows( nMatCols, nMatRows ); + aCol[nCol].Insert( nRow, pFCell ); + } + else if ( bMultiLine && aString.Search('\n') != STRING_NOTFOUND ) + PutCell( nCol, nRow, new ScEditCell( aString, pDocument ) ); + else + aCol[nCol].SetString(nRow, nTab, aString); + // pCell is invalid now (deleted) + } + } + return bFound; +} + +BOOL ScTable::Search(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, + const ScMarkData& rMark, String& rUndoStr, ScDocument* pUndoDoc) +{ + BOOL bFound = FALSE; + BOOL bAll = (rSearchItem.GetCommand() == SVX_SEARCHCMD_FIND_ALL) + ||(rSearchItem.GetCommand() == SVX_SEARCHCMD_REPLACE_ALL); + SCCOL nCol = rCol; + SCROW nRow = rRow; + SCCOL nLastCol; + SCROW nLastRow; + GetLastDataPos(nLastCol, nLastRow); + if (!bAll && rSearchItem.GetBackward()) + { + nCol = Min(nCol, (SCCOL)(nLastCol + 1)); + nRow = Min(nRow, (SCROW)(nLastRow + 1)); + if (rSearchItem.GetRowDirection()) + { + nCol--; + while (!bFound && ((SCsROW)nRow >= 0)) + { + while (!bFound && ((SCsCOL)nCol >= 0)) + { + bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (!bFound) + { + BOOL bIsEmpty; + do + { + nCol--; + if ((SCsCOL)nCol >= 0) + bIsEmpty = aCol[nCol].IsEmptyData(); + else + bIsEmpty = TRUE; + } + while (((SCsCOL)nCol >= 0) && bIsEmpty); + } + } + if (!bFound) + { + nCol = nLastCol; + nRow--; + } + } + } + else + { + nRow--; + while (!bFound && ((SCsCOL)nCol >= 0)) + { + while (!bFound && ((SCsROW)nRow >= 0)) + { + bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (!bFound) + { + if (!aCol[nCol].GetPrevDataPos(nRow)) + nRow = -1; + } + } + if (!bFound) + { + BOOL bIsEmpty; + nRow = nLastRow; + do + { + nCol--; + if ((SCsCOL)nCol >= 0) + bIsEmpty = aCol[nCol].IsEmptyData(); + else + bIsEmpty = TRUE; + } + while (((SCsCOL)nCol >= 0) && bIsEmpty); + } + } + } + } + else + { + if (!bAll && rSearchItem.GetRowDirection()) + { + nCol++; + while (!bFound && (nRow <= nLastRow)) + { + while (!bFound && (nCol <= nLastCol)) + { + bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (!bFound) + { + nCol++; + while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++; + } + } + if (!bFound) + { + nCol = 0; + nRow++; + } + } + } + else + { + nRow++; + while (!bFound && (nCol <= nLastCol)) + { + while (!bFound && (nRow <= nLastRow)) + { + bFound = SearchCell(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (!bFound) + { + if (!aCol[nCol].GetNextDataPos(nRow)) + nRow = MAXROW + 1; + } + } + if (!bFound) + { + nRow = 0; + nCol++; + while ((nCol <= nLastCol) && aCol[nCol].IsEmptyData()) nCol++; + } + } + } + } + if (bFound) + { + rCol = nCol; + rRow = nRow; + } + return bFound; +} + +BOOL ScTable::SearchAll(const SvxSearchItem& rSearchItem, ScMarkData& rMark, + String& rUndoStr, ScDocument* pUndoDoc) +{ + BOOL bFound = TRUE; + SCCOL nCol = 0; + SCROW nRow = -1; + + ScMarkData aNewMark( rMark ); // Tabellen-Markierungen kopieren + aNewMark.ResetMark(); + do + { + bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (bFound) + aNewMark.SetMultiMarkArea( ScRange( nCol, nRow, nTab ) ); + } + while (bFound); + + rMark = aNewMark; // Markierung kopieren + //! pro Tabelle + + return (aNewMark.IsMultiMarked()); +} + +BOOL ScTable::Replace(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, + const ScMarkData& rMark, String& rUndoStr, ScDocument* pUndoDoc) +{ + BOOL bFound = FALSE; + SCCOL nCol = rCol; + SCROW nRow = rRow; + if (rSearchItem.GetBackward()) + { + if (rSearchItem.GetRowDirection()) + nCol += 1; + else + nRow += 1; + } + else + { + if (rSearchItem.GetRowDirection()) + nCol -= 1; + else + nRow -= 1; + } + bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (bFound) + { + rCol = nCol; + rRow = nRow; + } + return bFound; +} + +BOOL ScTable::ReplaceAll(const SvxSearchItem& rSearchItem, ScMarkData& rMark, + String& rUndoStr, ScDocument* pUndoDoc) +{ + BOOL bOldDouble = ScColumn::bDoubleAlloc; // sollte immer FALSE sein? + DBG_ASSERT(!bOldDouble,"bDoubleAlloc ???"); + ScColumn::bDoubleAlloc = TRUE; // fuer Undo-Doc + + BOOL bFound = TRUE; + SCCOL nCol = 0; + SCROW nRow = -1; + + ScMarkData aNewMark( rMark ); // Tabellen-Markierungen kopieren + aNewMark.ResetMark(); + do + { + bFound = Search(rSearchItem, nCol, nRow, rMark, rUndoStr, pUndoDoc); + if (bFound) + aNewMark.SetMultiMarkArea( ScRange( nCol, nRow, nTab ) ); + } + while (bFound); + + ScColumn::bDoubleAlloc = bOldDouble; + + rMark = aNewMark; // Markierung kopieren + //! pro Tabelle + + return (aNewMark.IsMultiMarked()); +} + +BOOL ScTable::SearchStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, + ScMarkData& rMark) +{ + const ScStyleSheet* pSearchStyle = (const ScStyleSheet*) + pDocument->GetStyleSheetPool()->Find( + rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ); + + SCsCOL nCol = rCol; + SCsROW nRow = rRow; + BOOL bFound = FALSE; + + BOOL bSelect = rSearchItem.GetSelection(); + BOOL bRows = rSearchItem.GetRowDirection(); + BOOL bBack = rSearchItem.GetBackward(); + short nAdd = bBack ? -1 : 1; + + if (bRows) // zeilenweise + { + nRow += nAdd; + do + { + SCsROW nNextRow = aCol[nCol].SearchStyle( nRow, pSearchStyle, bBack, bSelect, rMark ); + if (!ValidRow(nNextRow)) + { + nRow = bBack ? MAXROW : 0; + nCol = sal::static_int_cast<SCsCOL>( nCol + nAdd ); + } + else + { + nRow = nNextRow; + bFound = TRUE; + } + } + while (!bFound && ValidCol(nCol)); + } + else // spaltenweise + { + SCsROW nNextRows[MAXCOLCOUNT]; + SCsCOL i; + for (i=0; i<=MAXCOL; i++) + { + SCsROW nSRow = nRow; + if (bBack) { if (i>=nCol) --nSRow; } + else { if (i<=nCol) ++nSRow; } + nNextRows[i] = aCol[i].SearchStyle( nSRow, pSearchStyle, bBack, bSelect, rMark ); + } + if (bBack) // rueckwaerts + { + nRow = -1; + for (i=MAXCOL; i>=0; i--) + if (nNextRows[i]>nRow) + { + nCol = i; + nRow = nNextRows[i]; + bFound = TRUE; + } + } + else // vorwaerts + { + nRow = MAXROW+1; + for (i=0; i<=MAXCOL; i++) + if (nNextRows[i]<nRow) + { + nCol = i; + nRow = nNextRows[i]; + bFound = TRUE; + } + } + } + + if (bFound) + { + rCol = (SCCOL) nCol; + rRow = (SCROW) nRow; + } + return bFound; +} + +//! einzelnes Pattern fuer Undo zurueckgeben + +BOOL ScTable::ReplaceStyle(const SvxSearchItem& rSearchItem, SCCOL& rCol, SCROW& rRow, + ScMarkData& rMark, BOOL bIsUndo) +{ + BOOL bRet; + if (bIsUndo) + bRet = TRUE; + else + bRet = SearchStyle(rSearchItem, rCol, rRow, rMark); + if (bRet) + { + const ScStyleSheet* pReplaceStyle = (const ScStyleSheet*) + pDocument->GetStyleSheetPool()->Find( + rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ); + + if (pReplaceStyle) + ApplyStyle( rCol, rRow, *pReplaceStyle ); + else + { + DBG_ERROR("pReplaceStyle==0"); + } + } + + return bRet; +} + +BOOL ScTable::SearchAllStyle(const SvxSearchItem& rSearchItem, ScMarkData& rMark) +{ + const ScStyleSheet* pSearchStyle = (const ScStyleSheet*) + pDocument->GetStyleSheetPool()->Find( + rSearchItem.GetSearchString(), SFX_STYLE_FAMILY_PARA ); + BOOL bSelect = rSearchItem.GetSelection(); + BOOL bBack = rSearchItem.GetBackward(); + + ScMarkData aNewMark( rMark ); // Tabellen-Markierungen kopieren + aNewMark.ResetMark(); + for (SCCOL i=0; i<=MAXCOL; i++) + { + BOOL bFound = TRUE; + SCsROW nRow = 0; + SCsROW nEndRow; + while (bFound && nRow <= MAXROW) + { + bFound = aCol[i].SearchStyleRange( nRow, nEndRow, pSearchStyle, bBack, bSelect, rMark ); + if (bFound) + { + if (nEndRow<nRow) + { + SCsROW nTemp = nRow; + nRow = nEndRow; + nEndRow = nTemp; + } + aNewMark.SetMultiMarkArea( ScRange( i,nRow,nTab, i,nEndRow,nTab ) ); + nRow = nEndRow + 1; + } + } + } + + rMark = aNewMark; // Markierung kopieren + //! pro Tabelle + + return (aNewMark.IsMultiMarked()); +} + +BOOL ScTable::ReplaceAllStyle(const SvxSearchItem& rSearchItem, ScMarkData& rMark, + ScDocument* pUndoDoc) +{ + BOOL bRet = SearchAllStyle(rSearchItem, rMark); + if (bRet) + { + const ScStyleSheet* pReplaceStyle = (const ScStyleSheet*) + pDocument->GetStyleSheetPool()->Find( + rSearchItem.GetReplaceString(), SFX_STYLE_FAMILY_PARA ); + + if (pReplaceStyle) + { + if (pUndoDoc) + pDocument->CopyToDocument( 0,0,nTab, MAXCOL,MAXROW,nTab, + IDF_ATTRIB, TRUE, pUndoDoc, &rMark ); + ApplySelectionStyle( *pReplaceStyle, rMark ); + } + else + { + DBG_ERROR("pReplaceStyle==0"); + } + } + + return bRet; +} + +BOOL ScTable::SearchAndReplace(const SvxSearchItem& rSearchItem, + SCCOL& rCol, SCROW& rRow, ScMarkData& rMark, + String& rUndoStr, ScDocument* pUndoDoc) +{ + USHORT nCommand = rSearchItem.GetCommand(); + BOOL bFound = FALSE; + if ( ValidColRow(rCol, rRow) || + ((nCommand == SVX_SEARCHCMD_FIND || nCommand == SVX_SEARCHCMD_REPLACE) && + (((rCol == MAXCOLCOUNT || rCol == -1) && VALIDROW(rRow)) || + ((rRow == MAXROWCOUNT || rRow == -1) && VALIDCOL(rCol)) + ) + ) + ) + { + BOOL bStyles = rSearchItem.GetPattern(); + if (bStyles) + { + if (nCommand == SVX_SEARCHCMD_FIND) + bFound = SearchStyle(rSearchItem, rCol, rRow, rMark); + else if (nCommand == SVX_SEARCHCMD_REPLACE) + bFound = ReplaceStyle(rSearchItem, rCol, rRow, rMark, FALSE); + else if (nCommand == SVX_SEARCHCMD_FIND_ALL) + bFound = SearchAllStyle(rSearchItem, rMark); + else if (nCommand == SVX_SEARCHCMD_REPLACE_ALL) + bFound = ReplaceAllStyle(rSearchItem, rMark, pUndoDoc); + } + else + { + // SearchParam no longer needed - SearchOptions contains all settings + com::sun::star::util::SearchOptions aSearchOptions = rSearchItem.GetSearchOptions(); + aSearchOptions.Locale = *ScGlobal::GetLocale(); + + // #107259# reflect UseAsianOptions flag in SearchOptions + // (use only ignore case and width if asian options are disabled). + // This is also done in SvxSearchDialog CommandHdl, but not in API object. + if ( !rSearchItem.IsUseAsianOptions() ) + aSearchOptions.transliterateFlags &= + ( com::sun::star::i18n::TransliterationModules_IGNORE_CASE | + com::sun::star::i18n::TransliterationModules_IGNORE_WIDTH ); + + pSearchText = new utl::TextSearch( aSearchOptions ); + + if (nCommand == SVX_SEARCHCMD_FIND) + bFound = Search(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc); + else if (nCommand == SVX_SEARCHCMD_FIND_ALL) + bFound = SearchAll(rSearchItem, rMark, rUndoStr, pUndoDoc); + else if (nCommand == SVX_SEARCHCMD_REPLACE) + bFound = Replace(rSearchItem, rCol, rRow, rMark, rUndoStr, pUndoDoc); + else if (nCommand == SVX_SEARCHCMD_REPLACE_ALL) + bFound = ReplaceAll(rSearchItem, rMark, rUndoStr, pUndoDoc); + + delete pSearchText; + pSearchText = NULL; + } + } + return bFound; +} + + + + + + diff --git a/sc/source/core/data/tabprotection.cxx b/sc/source/core/data/tabprotection.cxx new file mode 100644 index 000000000000..1620c5194e92 --- /dev/null +++ b/sc/source/core/data/tabprotection.cxx @@ -0,0 +1,465 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: tabprotection.cxx,v $ + * $Revision: 1.1.4.7 $ + * + * 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 "tabprotection.hxx" +#include "tools/debug.hxx" +#include "svtools/PasswordHelper.hxx" +#include "document.hxx" + +#define DEBUG_TAB_PROTECTION 0 + +using namespace ::com::sun::star; +using ::com::sun::star::uno::Sequence; +using ::rtl::OUString; + +// ============================================================================ + +bool ScPassHashHelper::needsPassHashRegen(const ScDocument& rDoc, ScPasswordHash eHash) +{ + if (rDoc.IsDocProtected()) + { + const ScDocProtection* p = rDoc.GetDocProtection(); + if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash)) + return true; + } + + SCTAB nTabCount = rDoc.GetTableCount(); + for (SCTAB i = 0; i < nTabCount; ++i) + { + const ScTableProtection* p = rDoc.GetTabProtection(i); + if (!p || !p->isProtected()) + // Sheet not protected. Skip it. + continue; + + if (!p->isPasswordEmpty() && !p->hasPasswordHash(eHash)) + return true; + } + + return false; +} + +// ============================================================================ + +ScPassHashProtectable::~ScPassHashProtectable() +{ +} + +// ============================================================================ + +static sal_uInt16 lcl_getXLHashFromChar(const sal_Char* szPassword) +{ + sal_uInt16 cchPassword = static_cast< sal_uInt16 >( strlen(szPassword) ); + sal_uInt16 wPasswordHash = 0; + if (!cchPassword) + return wPasswordHash; + + const char* pch = &szPassword[cchPassword]; + while (pch-- != szPassword) + { + wPasswordHash = ((wPasswordHash >> 14) & 0x01) | + ((wPasswordHash << 1) & 0x7fff); + wPasswordHash ^= *pch; + } + + wPasswordHash = ((wPasswordHash >> 14) & 0x01) | + ((wPasswordHash << 1) & 0x7fff); + + wPasswordHash ^= (0x8000 | ('N' << 8) | 'K'); + wPasswordHash ^= cchPassword; + + return wPasswordHash; +} + +static Sequence<sal_Int8> lcl_getXLHash(const String& aPassText) +{ + const sal_Char* szBuf = OUStringToOString(OUString(aPassText), RTL_TEXTENCODING_UTF8).getStr(); + sal_uInt16 nHash = lcl_getXLHashFromChar(szBuf); + Sequence<sal_Int8> aHash(2); + aHash[0] = (nHash >> 8) & 0xFF; + aHash[1] = nHash & 0xFF; + return aHash; +} + +class ScTableProtectionImpl +{ +public: + static ::com::sun::star::uno::Sequence<sal_Int8> hashPassword(const String& aPassText, ScPasswordHash eHash = PASSHASH_OOO); + + explicit ScTableProtectionImpl(SCSIZE nOptSize); + explicit ScTableProtectionImpl(const ScTableProtectionImpl& r); + + bool isProtected() const; + bool isProtectedWithPass() const; + void setProtected(bool bProtected); + + bool isPasswordEmpty() const; + bool hasPasswordHash(ScPasswordHash eHash) const; + void setPassword(const String& aPassText); + ::com::sun::star::uno::Sequence<sal_Int8> getPasswordHash(ScPasswordHash eHash) const; + void setPasswordHash(const ::com::sun::star::uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash = PASSHASH_OOO); + bool verifyPassword(const String& aPassText) const; + + bool isOptionEnabled(SCSIZE nOptId) const; + void setOption(SCSIZE nOptId, bool bEnabled); + +private: + String maPassText; + ::com::sun::star::uno::Sequence<sal_Int8> maPassHash; + ::std::vector<bool> maOptions; + bool mbEmptyPass; + bool mbProtected; + ScPasswordHash meHash; +}; + +Sequence<sal_Int8> ScTableProtectionImpl::hashPassword(const String& aPassText, ScPasswordHash eHash) +{ + Sequence<sal_Int8> aHash; + switch (eHash) + { + case PASSHASH_XL: + aHash = lcl_getXLHash(aPassText); + break; + case PASSHASH_OOO: + default: + SvPasswordHelper::GetHashPassword(aHash, aPassText); + break; + } + return aHash; +} + +ScTableProtectionImpl::ScTableProtectionImpl(SCSIZE nOptSize) : + maOptions(nOptSize), + mbEmptyPass(true), + mbProtected(false), + meHash(PASSHASH_OOO) +{ +} + +ScTableProtectionImpl::ScTableProtectionImpl(const ScTableProtectionImpl& r) : + maPassText(r.maPassText), + maPassHash(r.maPassHash), + maOptions(r.maOptions), + mbEmptyPass(r.mbEmptyPass), + mbProtected(r.mbProtected), + meHash(r.meHash) +{ +} + +bool ScTableProtectionImpl::isProtected() const +{ + return mbProtected; +} + +bool ScTableProtectionImpl::isProtectedWithPass() const +{ + if (!mbProtected) + return false; + + return maPassText.Len() || maPassHash.getLength(); +} + +void ScTableProtectionImpl::setProtected(bool bProtected) +{ + mbProtected = bProtected; + // We need to keep the old password even when the protection is off. So, + // don't erase the password data here. +} + +void ScTableProtectionImpl::setPassword(const String& aPassText) +{ + // We can't hash it here because we don't know whether this document will + // get saved to Excel or ODF, depending on which we will need to use a + // different hashing algorithm. One alternative is to hash it using all + // hash algorithms that we support, and store them all. + + maPassText = aPassText; + mbEmptyPass = aPassText.Len() == 0; + if (mbEmptyPass) + { + maPassHash = Sequence<sal_Int8>(); + } +} + +bool ScTableProtectionImpl::isPasswordEmpty() const +{ + return mbEmptyPass; +} + +bool ScTableProtectionImpl::hasPasswordHash(ScPasswordHash eHash) const +{ + if (mbEmptyPass) + return true; + + if (maPassText.Len()) + return true; + + if (meHash == eHash) + return true; + + return false; +} + +Sequence<sal_Int8> ScTableProtectionImpl::getPasswordHash(ScPasswordHash eHash) const +{ + if (mbEmptyPass) + // Flaged as empty. + return Sequence<sal_Int8>(); + + if (maPassText.Len()) + // Cleartext password exists. Hash it. + return hashPassword(maPassText, eHash); + + if (meHash == eHash) + // Stored hash exists. + return maPassHash; + + // Failed to find a matching hash. + return Sequence<sal_Int8>(); +} + +void ScTableProtectionImpl::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) +{ + sal_Int32 nLen = aPassword.getLength(); + mbEmptyPass = nLen <= 0 ? true : false; + meHash = eHash; + maPassHash = aPassword; + +#if DEBUG_TAB_PROTECTION + for (sal_Int32 i = 0; i < nLen; ++i) + printf("%2.2X ", static_cast<sal_uInt8>(aPassword[i])); + printf("\n"); +#endif +} + +bool ScTableProtectionImpl::verifyPassword(const String& aPassText) const +{ +#if DEBUG_TAB_PROTECTION + fprintf(stdout, "ScTableProtectionImpl::verifyPassword: input = '%s'\n", + OUStringToOString(rtl::OUString(aPassText), RTL_TEXTENCODING_UTF8).getStr()); +#endif + + if (mbEmptyPass) + return aPassText.Len() == 0; + + if (maPassText.Len()) + // Clear text password exists, and this one takes precedence. + return aPassText.Equals(maPassText); + + Sequence<sal_Int8> aHash = hashPassword(aPassText, meHash); + +#if DEBUG_TAB_PROTECTION + fprintf(stdout, "ScTableProtectionImpl::verifyPassword: hash = "); + for (sal_Int32 i = 0; i < aHash.getLength(); ++i) + printf("%2.2X ", static_cast<sal_uInt8>(aHash[i])); + printf("\n"); +#endif + + return aHash == maPassHash; +} + +bool ScTableProtectionImpl::isOptionEnabled(SCSIZE nOptId) const +{ + if ( maOptions.size() <= static_cast<size_t>(nOptId) ) + { + DBG_ERROR("ScTableProtectionImpl::isOptionEnabled: wrong size"); + return false; + } + + return maOptions[nOptId]; +} + +void ScTableProtectionImpl::setOption(SCSIZE nOptId, bool bEnabled) +{ + if ( maOptions.size() <= static_cast<size_t>(nOptId) ) + { + DBG_ERROR("ScTableProtectionImpl::setOption: wrong size"); + return; + } + + maOptions[nOptId] = bEnabled; +} + +// ============================================================================ + +ScDocProtection::ScDocProtection() : + mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScDocProtection::NONE))) +{ +} + +ScDocProtection::ScDocProtection(const ScDocProtection& r) : + ScPassHashProtectable(), + mpImpl(new ScTableProtectionImpl(*r.mpImpl)) +{ +} + +ScDocProtection::~ScDocProtection() +{ +} + +bool ScDocProtection::isProtected() const +{ + return mpImpl->isProtected(); +} + +bool ScDocProtection::isProtectedWithPass() const +{ + return mpImpl->isProtectedWithPass(); +} + +void ScDocProtection::setProtected(bool bProtected) +{ + mpImpl->setProtected(bProtected); + + // Currently Calc doesn't support document protection options. So, let's + // assume that when the document is protected, its structure is protected. + // We need to do this for Excel export. + mpImpl->setOption(ScDocProtection::STRUCTURE, bProtected); +} + +bool ScDocProtection::isPasswordEmpty() const +{ + return mpImpl->isPasswordEmpty(); +} + +bool ScDocProtection::hasPasswordHash(ScPasswordHash eHash) const +{ + return mpImpl->hasPasswordHash(eHash); +} + +void ScDocProtection::setPassword(const String& aPassText) +{ + mpImpl->setPassword(aPassText); +} + +uno::Sequence<sal_Int8> ScDocProtection::getPasswordHash(ScPasswordHash eHash) const +{ + return mpImpl->getPasswordHash(eHash); +} + +void ScDocProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) +{ + mpImpl->setPasswordHash(aPassword, eHash); +} + +bool ScDocProtection::verifyPassword(const String& aPassText) const +{ + return mpImpl->verifyPassword(aPassText); +} + +bool ScDocProtection::isOptionEnabled(Option eOption) const +{ + return mpImpl->isOptionEnabled(eOption); +} + +void ScDocProtection::setOption(Option eOption, bool bEnabled) +{ + mpImpl->setOption(eOption, bEnabled); +} + +// ============================================================================ + +ScTableProtection::ScTableProtection() : + mpImpl(new ScTableProtectionImpl(static_cast<SCSIZE>(ScTableProtection::NONE))) +{ + // Set default values for the options. + mpImpl->setOption(SELECT_LOCKED_CELLS, true); + mpImpl->setOption(SELECT_UNLOCKED_CELLS, true); +} + +ScTableProtection::ScTableProtection(const ScTableProtection& r) : + ScPassHashProtectable(), + mpImpl(new ScTableProtectionImpl(*r.mpImpl)) +{ +} + +ScTableProtection::~ScTableProtection() +{ +} + +bool ScTableProtection::isProtected() const +{ + return mpImpl->isProtected(); +} + +bool ScTableProtection::isProtectedWithPass() const +{ + return mpImpl->isProtectedWithPass(); +} + +void ScTableProtection::setProtected(bool bProtected) +{ + mpImpl->setProtected(bProtected); +} + +bool ScTableProtection::isPasswordEmpty() const +{ + return mpImpl->isPasswordEmpty(); +} + +bool ScTableProtection::hasPasswordHash(ScPasswordHash eHash) const +{ + return mpImpl->hasPasswordHash(eHash); +} + +void ScTableProtection::setPassword(const String& aPassText) +{ + mpImpl->setPassword(aPassText); +} + +Sequence<sal_Int8> ScTableProtection::getPasswordHash(ScPasswordHash eHash) const +{ + return mpImpl->getPasswordHash(eHash); +} + +void ScTableProtection::setPasswordHash(const uno::Sequence<sal_Int8>& aPassword, ScPasswordHash eHash) +{ + mpImpl->setPasswordHash(aPassword, eHash); +} + +bool ScTableProtection::verifyPassword(const String& aPassText) const +{ + return mpImpl->verifyPassword(aPassText); +} + +bool ScTableProtection::isOptionEnabled(Option eOption) const +{ + return mpImpl->isOptionEnabled(eOption); +} + +void ScTableProtection::setOption(Option eOption, bool bEnabled) +{ + mpImpl->setOption(eOption, bEnabled); +} + diff --git a/sc/source/core/data/userdat.cxx b/sc/source/core/data/userdat.cxx new file mode 100644 index 000000000000..66397700676c --- /dev/null +++ b/sc/source/core/data/userdat.cxx @@ -0,0 +1,130 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: userdat.cxx,v $ + * $Revision: 1.12.128.1 $ + * + * 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 "userdat.hxx" +#include <tools/debug.hxx> +#include "drwlayer.hxx" +#include "rechead.hxx" + +// ----------------------------------------------------------------------- + +ScDrawObjFactory::ScDrawObjFactory() +{ + SdrObjFactory::InsertMakeUserDataHdl( LINK ( this, ScDrawObjFactory, MakeUserData ) ); +} + +ScDrawObjFactory::~ScDrawObjFactory() +{ + SdrObjFactory::RemoveMakeUserDataHdl( LINK ( this, ScDrawObjFactory, MakeUserData ) ); +} + +IMPL_LINK_INLINE_START( ScDrawObjFactory, MakeUserData, SdrObjFactory *, pObjFactory ) +{ + if ( pObjFactory->nInventor == SC_DRAWLAYER ) + { + if ( pObjFactory->nIdentifier == SC_UD_OBJDATA ) + pObjFactory->pNewData = new ScDrawObjData; + else if ( pObjFactory->nIdentifier == SC_UD_IMAPDATA ) + pObjFactory->pNewData = new ScIMapInfo; + else if ( pObjFactory->nIdentifier == SC_UD_MACRODATA ) + pObjFactory->pNewData = new ScMacroInfo; + else + { + DBG_ERROR("MakeUserData: falsche ID"); + } + } + return 0; +} +IMPL_LINK_INLINE_END( ScDrawObjFactory, MakeUserData, SdrObjFactory *, pObjFactory ) + +//------------------------------------------------------------------------ + +ScDrawObjData::ScDrawObjData() : + SdrObjUserData( SC_DRAWLAYER, SC_UD_OBJDATA, 0 ), + maStart( ScAddress::INITIALIZE_INVALID ), + maEnd( ScAddress::INITIALIZE_INVALID ), + mbNote( false ) +{ +} + +ScDrawObjData* ScDrawObjData::Clone( SdrObject* ) const +{ + return new ScDrawObjData( *this ); +} + +//------------------------------------------------------------------------ + +ScIMapInfo::ScIMapInfo() : + SdrObjUserData( SC_DRAWLAYER, SC_UD_IMAPDATA, 0 ) +{ +} + +ScIMapInfo::ScIMapInfo( const ImageMap& rImageMap ) : + SdrObjUserData( SC_DRAWLAYER, SC_UD_IMAPDATA, 0 ), + aImageMap( rImageMap ) +{ +} + +ScIMapInfo::ScIMapInfo( const ScIMapInfo& rIMapInfo ) : + SdrObjUserData( rIMapInfo ), + aImageMap( rIMapInfo.aImageMap ) +{ +} + +ScIMapInfo::~ScIMapInfo() +{ +} + +SdrObjUserData* ScIMapInfo::Clone( SdrObject* ) const +{ + return new ScIMapInfo( *this ); +} + +//------------------------------------------------------------------------ + +ScMacroInfo::ScMacroInfo() : + SdrObjUserData( SC_DRAWLAYER, SC_UD_MACRODATA, 0 ) +{ +} + +ScMacroInfo::~ScMacroInfo() +{ +} + +SdrObjUserData* ScMacroInfo::Clone( SdrObject* /*pObj*/ ) const +{ + return new ScMacroInfo( *this ); +} + diff --git a/sc/source/core/data/validat.cxx b/sc/source/core/data/validat.cxx new file mode 100644 index 000000000000..443474060c3e --- /dev/null +++ b/sc/source/core/data/validat.cxx @@ -0,0 +1,999 @@ +/************************************************************************* + * + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * Copyright 2008 by Sun Microsystems, Inc. + * + * OpenOffice.org - a multi-platform office productivity suite + * + * $RCSfile: validat.cxx,v $ + * $Revision: 1.24.110.2 $ + * + * 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 "scitems.hxx" +#include <sfx2/app.hxx> +#include <sfx2/docfile.hxx> +#include <sfx2/objsh.hxx> +#include <basic/sbmeth.hxx> +#include <basic/sbmod.hxx> +#include <basic/sbstar.hxx> +#include <basic/basmgr.hxx> + +#include <basic/sbx.hxx> +#include <svtools/zforlist.hxx> +#include <vcl/msgbox.hxx> +#include <tools/urlobj.hxx> +#include <rtl/math.hxx> + +#include "validat.hxx" +#include "document.hxx" +#include "cell.hxx" +#include "patattr.hxx" +#include "rechead.hxx" +#include "globstr.hrc" +#include "rangenam.hxx" +#include "dbcolect.hxx" + +#include <math.h> +#include <memory> + +using namespace formula; +//------------------------------------------------------------------------ + +SV_IMPL_OP_PTRARR_SORT( ScValidationEntries_Impl, ScValidationDataPtr ); + +//------------------------------------------------------------------------ + +// +// Eintrag fuer Gueltigkeit (es gibt nur eine Bedingung) +// + +ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, + const String& rExpr1, const String& rExpr2, + ScDocument* pDocument, const ScAddress& rPos, + const String& rExprNmsp1, const String& rExprNmsp2, + FormulaGrammar::Grammar eGrammar1, FormulaGrammar::Grammar eGrammar2 ) : + ScConditionEntry( eOper, rExpr1, rExpr2, pDocument, rPos, rExprNmsp1, rExprNmsp2, eGrammar1, eGrammar2 ), + nKey( 0 ), + eDataMode( eMode ), + eErrorStyle( SC_VALERR_STOP ), + mnListType( ValidListType::UNSORTED ) +{ + bShowInput = bShowError = FALSE; +} + +ScValidationData::ScValidationData( ScValidationMode eMode, ScConditionMode eOper, + const ScTokenArray* pArr1, const ScTokenArray* pArr2, + ScDocument* pDocument, const ScAddress& rPos ) : + ScConditionEntry( eOper, pArr1, pArr2, pDocument, rPos ), + nKey( 0 ), + eDataMode( eMode ), + eErrorStyle( SC_VALERR_STOP ), + mnListType( ValidListType::UNSORTED ) +{ + bShowInput = bShowError = FALSE; +} + +ScValidationData::ScValidationData( const ScValidationData& r ) : + ScConditionEntry( r ), + nKey( r.nKey ), + eDataMode( r.eDataMode ), + bShowInput( r.bShowInput ), + bShowError( r.bShowError ), + eErrorStyle( r.eErrorStyle ), + mnListType( r.mnListType ), + aInputTitle( r.aInputTitle ), + aInputMessage( r.aInputMessage ), + aErrorTitle( r.aErrorTitle ), + aErrorMessage( r.aErrorMessage ) +{ + // Formeln per RefCount kopiert +} + +ScValidationData::ScValidationData( ScDocument* pDocument, const ScValidationData& r ) : + ScConditionEntry( pDocument, r ), + nKey( r.nKey ), + eDataMode( r.eDataMode ), + bShowInput( r.bShowInput ), + bShowError( r.bShowError ), + eErrorStyle( r.eErrorStyle ), + mnListType( r.mnListType ), + aInputTitle( r.aInputTitle ), + aInputMessage( r.aInputMessage ), + aErrorTitle( r.aErrorTitle ), + aErrorMessage( r.aErrorMessage ) +{ + // Formeln wirklich kopiert +} + +ScValidationData::~ScValidationData() +{ +} + +BOOL ScValidationData::IsEmpty() const +{ + String aEmpty; + ScValidationData aDefault( SC_VALID_ANY, SC_COND_EQUAL, aEmpty, aEmpty, GetDocument(), ScAddress() ); + return EqualEntries( aDefault ); +} + +BOOL ScValidationData::EqualEntries( const ScValidationData& r ) const +{ + // gleiche Parameter eingestellt (ohne Key) + + return ScConditionEntry::operator==(r) && + eDataMode == r.eDataMode && + bShowInput == r.bShowInput && + bShowError == r.bShowError && + eErrorStyle == r.eErrorStyle && + mnListType == r.mnListType && + aInputTitle == r.aInputTitle && + aInputMessage == r.aInputMessage && + aErrorTitle == r.aErrorTitle && + aErrorMessage == r.aErrorMessage; +} + +void ScValidationData::ResetInput() +{ + bShowInput = FALSE; +} + +void ScValidationData::ResetError() +{ + bShowError = FALSE; +} + +void ScValidationData::SetInput( const String& rTitle, const String& rMsg ) +{ + bShowInput = TRUE; + aInputTitle = rTitle; + aInputMessage = rMsg; +} + +void ScValidationData::SetError( const String& rTitle, const String& rMsg, + ScValidErrorStyle eStyle ) +{ + bShowError = TRUE; + eErrorStyle = eStyle; + aErrorTitle = rTitle; + aErrorMessage = rMsg; +} + +BOOL ScValidationData::GetErrMsg( String& rTitle, String& rMsg, + ScValidErrorStyle& rStyle ) const +{ + rTitle = aErrorTitle; + rMsg = aErrorMessage; + rStyle = eErrorStyle; + return bShowError; +} + +BOOL ScValidationData::DoScript( const ScAddress& rPos, const String& rInput, + ScFormulaCell* pCell, Window* pParent ) const +{ + ScDocument* pDocument = GetDocument(); + SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); + if ( !pDocSh || !pDocument->CheckMacroWarn() ) + return FALSE; + + BOOL bScriptReturnedFalse = FALSE; // Standard: kein Abbruch + + // Set up parameters + ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aParams(2); + + // 1) eingegebener / berechneter Wert + String aValStr = rInput; + double nValue; + BOOL bIsValue = FALSE; + if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen + { + bIsValue = pCell->IsValue(); + if ( bIsValue ) + nValue = pCell->GetValue(); + else + pCell->GetString( aValStr ); + } + if ( bIsValue ) + aParams[0] = ::com::sun::star::uno::makeAny( nValue ); + else + aParams[0] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aValStr ) ); + + // 2) Position der Zelle + String aPosStr; + rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); + aParams[1] = ::com::sun::star::uno::makeAny( ::rtl::OUString( aPosStr ) ); + + // use link-update flag to prevent closing the document + // while the macro is running + BOOL bWasInLinkUpdate = pDocument->IsInLinkUpdate(); + if ( !bWasInLinkUpdate ) + pDocument->SetInLinkUpdate( TRUE ); + + if ( pCell ) + pDocument->LockTable( rPos.Tab() ); + + ::com::sun::star::uno::Any aRet; + ::com::sun::star::uno::Sequence< sal_Int16 > aOutArgsIndex; + ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Any > aOutArgs; + + ErrCode eRet = pDocSh->CallXScript( + aErrorTitle, aParams, aRet, aOutArgsIndex, aOutArgs ); + + if ( pCell ) + pDocument->UnlockTable( rPos.Tab() ); + + if ( !bWasInLinkUpdate ) + pDocument->SetInLinkUpdate( FALSE ); + + // Check the return value from the script + // The contents of the cell get reset if the script returns false + BOOL bTmp = FALSE; + if ( eRet == ERRCODE_NONE && + aRet.getValueType() == getCppuBooleanType() && + sal_True == ( aRet >>= bTmp ) && + bTmp == FALSE ) + { + bScriptReturnedFalse = TRUE; + } + + if ( eRet == ERRCODE_BASIC_METHOD_NOT_FOUND && !pCell ) + // Makro nicht gefunden (nur bei Eingabe) + { + //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? + + ErrorBox aBox( pParent, WinBits(WB_OK), + ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); + aBox.Execute(); + } + + return bScriptReturnedFalse; +} + + // TRUE -> Abbruch + +BOOL ScValidationData::DoMacro( const ScAddress& rPos, const String& rInput, + ScFormulaCell* pCell, Window* pParent ) const +{ + if ( SfxApplication::IsXScriptURL( aErrorTitle ) ) + { + return DoScript( rPos, rInput, pCell, pParent ); + } + + ScDocument* pDocument = GetDocument(); + SfxObjectShell* pDocSh = pDocument->GetDocumentShell(); + if ( !pDocSh || !pDocument->CheckMacroWarn() ) + return FALSE; + + BOOL bDone = FALSE; + BOOL bRet = FALSE; // Standard: kein Abbruch + SfxApplication* pSfxApp = SFX_APP(); + pSfxApp->EnterBasicCall(); // Dok-Basic anlegen etc. + + // Wenn das Dok waehrend eines Basic-Calls geladen wurde, + // ist das Sbx-Objekt evtl. nicht angelegt (?) +// pDocSh->GetSbxObject(); + + // keine Sicherheitsabfrage mehr vorneweg (nur CheckMacroWarn), das passiert im CallBasic + +#if 0 + // Makro-Name liegt in folgender Form vor: + // "Macroname.Modulname.Libname.Dokumentname" oder + // "Macroname.Modulname.Libname.Applikationsname" + String aMacroName = aErrorTitle.GetToken(0, '.'); + String aModulName = aErrorTitle.GetToken(1, '.'); + String aLibName = aErrorTitle.GetToken(2, '.'); + String aDocName = aErrorTitle.GetToken(3, '.'); +#endif + + // Funktion ueber den einfachen Namen suchen, + // dann aBasicStr, aMacroStr fuer SfxObjectShell::CallBasic zusammenbauen + + StarBASIC* pRoot = pDocSh->GetBasic(); + SbxVariable* pVar = pRoot->Find( aErrorTitle, SbxCLASS_METHOD ); + if ( pVar && pVar->ISA(SbMethod) ) + { + SbMethod* pMethod = (SbMethod*)pVar; + SbModule* pModule = pMethod->GetModule(); + SbxObject* pObject = pModule->GetParent(); + String aMacroStr = pObject->GetName(); + aMacroStr += '.'; + aMacroStr += pModule->GetName(); + aMacroStr += '.'; + aMacroStr += pMethod->GetName(); + String aBasicStr; + + // #95867# the distinction between document- and app-basic has to be done + // by checking the parent (as in ScInterpreter::ScMacro), not by looping + // over all open documents, because this may be called from within loading, + // when SfxObjectShell::GetFirst/GetNext won't find the document. + + if ( pObject->GetParent() ) + aBasicStr = pObject->GetParent()->GetName(); // Dokumentenbasic + else + aBasicStr = SFX_APP()->GetName(); // Applikationsbasic + + // Parameter fuer Makro + SbxArrayRef refPar = new SbxArray; + + // 1) eingegebener / berechneter Wert + String aValStr = rInput; + double nValue = 0.0; + BOOL bIsValue = FALSE; + if ( pCell ) // wenn Zelle gesetzt, aus Interpret gerufen + { + bIsValue = pCell->IsValue(); + if ( bIsValue ) + nValue = pCell->GetValue(); + else + pCell->GetString( aValStr ); + } + if ( bIsValue ) + refPar->Get(1)->PutDouble( nValue ); + else + refPar->Get(1)->PutString( aValStr ); + + // 2) Position der Zelle + String aPosStr; + rPos.Format( aPosStr, SCA_VALID | SCA_TAB_3D, pDocument, pDocument->GetAddressConvention() ); + refPar->Get(2)->PutString( aPosStr ); + + // use link-update flag to prevent closing the document + // while the macro is running + BOOL bWasInLinkUpdate = pDocument->IsInLinkUpdate(); + if ( !bWasInLinkUpdate ) + pDocument->SetInLinkUpdate( TRUE ); + + if ( pCell ) + pDocument->LockTable( rPos.Tab() ); + SbxVariableRef refRes = new SbxVariable; + ErrCode eRet = pDocSh->CallBasic( aMacroStr, aBasicStr, NULL, refPar, refRes ); + if ( pCell ) + pDocument->UnlockTable( rPos.Tab() ); + + if ( !bWasInLinkUpdate ) + pDocument->SetInLinkUpdate( FALSE ); + + // Eingabe abbrechen, wenn Basic-Makro FALSE zurueckgibt + if ( eRet == ERRCODE_NONE && refRes->GetType() == SbxBOOL && refRes->GetBool() == FALSE ) + bRet = TRUE; + bDone = TRUE; + } + pSfxApp->LeaveBasicCall(); + + if ( !bDone && !pCell ) // Makro nicht gefunden (nur bei Eingabe) + { + //! andere Fehlermeldung, wenn gefunden, aber nicht bAllowed ?? + + ErrorBox aBox( pParent, WinBits(WB_OK), + ScGlobal::GetRscString( STR_VALID_MACRONOTFOUND ) ); + aBox.Execute(); + } + + return bRet; +} + +void ScValidationData::DoCalcError( ScFormulaCell* pCell ) const +{ + if ( eErrorStyle == SC_VALERR_MACRO ) + DoMacro( pCell->aPos, EMPTY_STRING, pCell, NULL ); +} + + // TRUE -> Abbruch + +BOOL ScValidationData::DoError( Window* pParent, const String& rInput, + const ScAddress& rPos ) const +{ + if ( eErrorStyle == SC_VALERR_MACRO ) + return DoMacro( rPos, rInput, NULL, pParent ); + + // Fehlermeldung ausgeben + + String aTitle = aErrorTitle; + if (!aTitle.Len()) + aTitle = ScGlobal::GetRscString( STR_MSSG_DOSUBTOTALS_0 ); // application title + String aMessage = aErrorMessage; + if (!aMessage.Len()) + aMessage = ScGlobal::GetRscString( STR_VALID_DEFERROR ); + + //! ErrorBox / WarningBox / InfoBox ? + //! (bei InfoBox immer nur OK-Button) + + WinBits nStyle = 0; + switch (eErrorStyle) + { + case SC_VALERR_STOP: + nStyle = WB_OK | WB_DEF_OK; + break; + case SC_VALERR_WARNING: + nStyle = WB_OK_CANCEL | WB_DEF_CANCEL; + break; + case SC_VALERR_INFO: + nStyle = WB_OK_CANCEL | WB_DEF_OK; + break; + default: + { + // added to avoid warnings + } + } + + MessBox aBox( pParent, WinBits(nStyle), aTitle, aMessage ); + USHORT nRet = aBox.Execute(); + + return ( eErrorStyle == SC_VALERR_STOP || nRet == RET_CANCEL ); +} + + +BOOL ScValidationData::IsDataValid( const String& rTest, const ScPatternAttr& rPattern, + const ScAddress& rPos ) const +{ + if ( eDataMode == SC_VALID_ANY ) + return TRUE; // alles erlaubt + + if ( rTest.GetChar(0) == '=' ) + return FALSE; // Formeln sind sonst immer ungueltig + + if ( !rTest.Len() ) + return IsIgnoreBlank(); // leer: wie eingestellt + + SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); + + // Test, was es denn ist - wie in ScColumn::SetString + + sal_uInt32 nFormat = rPattern.GetNumberFormat( pFormatter ); + + double nVal; + BOOL bIsVal = pFormatter->IsNumberFormat( rTest, nFormat, nVal ); + ScBaseCell* pCell; + if (bIsVal) + pCell = new ScValueCell( nVal ); + else + pCell = new ScStringCell( rTest ); + + BOOL bRet = IsDataValid( pCell, rPos ); + + pCell->Delete(); + return bRet; +} + +BOOL ScValidationData::IsDataValid( ScBaseCell* pCell, const ScAddress& rPos ) const +{ + if( eDataMode == SC_VALID_LIST ) + return IsListValid( pCell, rPos ); + + double nVal = 0.0; + String aString; + BOOL bIsVal = TRUE; + + switch (pCell->GetCellType()) + { + case CELLTYPE_VALUE: + nVal = ((ScValueCell*)pCell)->GetValue(); + break; + case CELLTYPE_STRING: + ((ScStringCell*)pCell)->GetString( aString ); + bIsVal = FALSE; + break; + case CELLTYPE_EDIT: + ((ScEditCell*)pCell)->GetString( aString ); + bIsVal = FALSE; + break; + case CELLTYPE_FORMULA: + { + ScFormulaCell* pFCell = (ScFormulaCell*)pCell; + bIsVal = pFCell->IsValue(); + if ( bIsVal ) + nVal = pFCell->GetValue(); + else + pFCell->GetString( aString ); + } + break; + default: // Notizen, Broadcaster + return IsIgnoreBlank(); // wie eingestellt + } + + BOOL bOk = TRUE; + switch (eDataMode) + { + // SC_VALID_ANY schon oben + + case SC_VALID_WHOLE: + case SC_VALID_DECIMAL: + case SC_VALID_DATE: // Date/Time ist nur Formatierung + case SC_VALID_TIME: + bOk = bIsVal; + if ( bOk && eDataMode == SC_VALID_WHOLE ) + bOk = ::rtl::math::approxEqual( nVal, floor(nVal+0.5) ); // ganze Zahlen + if ( bOk ) + bOk = IsCellValid( pCell, rPos ); + break; + + case SC_VALID_CUSTOM: + // fuer Custom muss eOp == SC_COND_DIRECT sein + //! der Wert muss im Dokument stehen !!!!!!!!!!!!!!!!!!!! + bOk = IsCellValid( pCell, rPos ); + break; + + case SC_VALID_TEXTLEN: + bOk = !bIsVal; // nur Text + if ( bOk ) + { + double nLenVal = (double) aString.Len(); + ScValueCell aTmpCell( nLenVal ); + bOk = IsCellValid( &aTmpCell, rPos ); + } + break; + + default: + DBG_ERROR("hammanochnich"); + break; + } + + return bOk; +} + +// ---------------------------------------------------------------------------- + +namespace { + +/** Token array helper. Iterates over all string tokens. + @descr The token array must contain separated string tokens only. + @param bSkipEmpty true = Ignores string tokens with empty strings. */ +class ScStringTokenIterator +{ +public: + inline explicit ScStringTokenIterator( ScTokenArray& rTokArr, bool bSkipEmpty = true ) : + mrTokArr( rTokArr ), mbSkipEmpty( bSkipEmpty ), mbOk( true ) {} + + /** Returns the string of the first string token or NULL on error or empty token array. */ + const String* First(); + /** Returns the string of the next string token or NULL on error or end of token array. */ + const String* Next(); + + /** Returns false, if a wrong token has been found. Does NOT return false on end of token array. */ + inline bool Ok() const { return mbOk; } + +private: + ScTokenArray& mrTokArr; /// The token array for iteration. + bool mbSkipEmpty; /// Ignore empty strings. + bool mbOk; /// true = correct token or end of token array. +}; + +const String* ScStringTokenIterator::First() +{ + mrTokArr.Reset(); + mbOk = true; + return Next(); +} + +const String* ScStringTokenIterator::Next() +{ + if( !mbOk ) + return NULL; + + // seek to next non-separator token + const FormulaToken* pToken = mrTokArr.NextNoSpaces(); + while( pToken && (pToken->GetOpCode() == ocSep) ) + pToken = mrTokArr.NextNoSpaces(); + + mbOk = !pToken || (pToken->GetType() == formula::svString); + const String* pString = (mbOk && pToken) ? &pToken->GetString() : NULL; + // string found but empty -> get next token; otherwise return it + return (mbSkipEmpty && pString && !pString->Len()) ? Next() : pString; +} + +// ---------------------------------------------------------------------------- + +/** Returns the number format of the passed cell, or the standard format. */ +ULONG lclGetCellFormat( ScDocument& rDoc, const ScAddress& rPos ) +{ + const ScPatternAttr* pPattern = rDoc.GetPattern( rPos.Col(), rPos.Row(), rPos.Tab() ); + if( !pPattern ) + pPattern = rDoc.GetDefPattern(); + return pPattern->GetNumberFormat( rDoc.GetFormatTable() ); +} + +/** Inserts the passed string object. Always takes ownership. pData is invalid after this call! */ +void lclInsertStringToCollection( TypedScStrCollection& rStrColl, TypedStrData* pData, bool bSorted ) +{ + if( !(bSorted ? rStrColl.Insert( pData ) : rStrColl.AtInsert( rStrColl.GetCount(), pData )) ) + delete pData; +} + +} // namespace + +// ---------------------------------------------------------------------------- + +bool ScValidationData::HasSelectionList() const +{ + return (eDataMode == SC_VALID_LIST) && (mnListType != ValidListType::INVISIBLE); +} + +bool ScValidationData::GetSelectionFromFormula( TypedScStrCollection* pStrings, + ScBaseCell* pCell, + const ScAddress& rPos, + const ScTokenArray& rTokArr, + int& rMatch ) const +{ + bool bOk = true; + + // pDoc is private in condition, use an accessor and a long winded name. + ScDocument* pDocument = GetDocument(); + if( NULL == pDocument ) + return false; + + ScFormulaCell aValidationSrc( pDocument, rPos, &rTokArr, + formula::FormulaGrammar::GRAM_DEFAULT, MM_FORMULA); + + // Make sure the formula gets interpreted and a result is delivered, + // regardless of the AutoCalc setting. + aValidationSrc.Interpret(); + + ScMatrixRef xMatRef; + const ScMatrix *pValues = aValidationSrc.GetMatrix(); + if (!pValues) + { + // The somewhat nasty case of either an error occured, or the + // dereferenced value of a single cell reference or an immediate result + // is stored as a single value. + + // Use an interim matrix to create the TypedStrData below. + xMatRef = new ScMatrix(1,1); + + USHORT nErrCode = aValidationSrc.GetErrCode(); + if (nErrCode) + { + /* TODO : to use later in an alert box? + * String rStrResult = "..."; + * rStrResult += ScGlobal::GetLongErrorString(nErrCode); + */ + + xMatRef->PutError( nErrCode, 0); + bOk = false; + } + else if (aValidationSrc.HasValueData()) + xMatRef->PutDouble( aValidationSrc.GetValue(), 0); + else + { + String aStr; + aValidationSrc.GetString( aStr); + xMatRef->PutString( aStr, 0); + } + + pValues = xMatRef; + } + + // which index matched. We will want it eventually to pre-select that item. + rMatch = -1; + + SvNumberFormatter* pFormatter = GetDocument()->GetFormatTable(); + + bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); + SCSIZE nCol, nRow, nCols, nRows, n = 0; + pValues->GetDimensions( nCols, nRows ); + + BOOL bRef = FALSE; + ScRange aRange; + + ScTokenArray* pArr = (ScTokenArray*) &rTokArr; + pArr->Reset(); + ScToken* t = NULL; + if (pArr->GetLen() == 1 && (t = static_cast<ScToken*>(pArr->GetNextReferenceOrName())) != NULL) + { + if (t->GetOpCode() == ocDBArea) + { + if( ScDBData* pDBData = pDocument->GetDBCollection()->FindIndex( t->GetIndex() ) ) + { + pDBData->GetArea(aRange); + bRef = TRUE; + } + } + else if (t->GetOpCode() == ocName) + { + ScRangeData* pName = pDocument->GetRangeName()->FindIndex( t->GetIndex() ); + if (pName && pName->IsReference(aRange)) + { + bRef = TRUE; + } + } + else if (t->GetType() != svIndex) + { + t->CalcAbsIfRel(rPos); + if (pArr->IsValidReference(aRange)) + { + bRef = TRUE; + } + } + } + + /* XL artificially limits things to a single col or row in the UI but does + * not list the constraint in MOOXml. If a defined name or INDIRECT + * resulting in 1D is entered in the UI and the definition later modified + * to 2D, it is evaluated fine and also stored and loaded. Lets get ahead + * of the curve and support 2d. In XL, values are listed row-wise, do the + * same. */ + for( nRow = 0; nRow < nRows ; nRow++ ) + { + for( nCol = 0; nCol < nCols ; nCol++ ) + { + ScTokenArray aCondTokArr; + TypedStrData* pEntry = NULL; + ScMatValType nMatValType; + String aValStr; + const ScMatrixValue* pMatVal = pValues->Get( nCol, nRow, nMatValType); + + // strings and empties + if( NULL == pMatVal || ScMatrix::IsNonValueType( nMatValType ) ) + { + if( NULL != pMatVal ) + aValStr = pMatVal->GetString(); + + if( NULL != pStrings ) + pEntry = new TypedStrData( aValStr, 0.0, SC_STRTYPE_STANDARD); + + if( pCell && rMatch < 0 ) + aCondTokArr.AddString( aValStr ); + } + else + { + USHORT nErr = pMatVal->GetError(); + + if( 0 != nErr ) + { + aValStr = ScGlobal::GetErrorString( nErr ); + } + else + { + // FIXME FIXME FIXME + // Feature regression. Date formats are lost passing through the matrix + //pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); + //For external reference and a formula that results in an area or array, date formats are still lost. + if ( bRef ) + { + pDocument->GetInputString((SCCOL)(nCol+aRange.aStart.Col()), + (SCROW)(nRow+aRange.aStart.Row()), aRange.aStart.Tab() , aValStr); + } + else + pFormatter->GetInputLineString( pMatVal->fVal, 0, aValStr ); + } + + if( pCell && rMatch < 0 ) + { + // I am not sure errors will work here, but a user can no + // manually enter an error yet so the point is somewhat moot. + aCondTokArr.AddDouble( pMatVal->fVal ); + } + if( NULL != pStrings ) + pEntry = new TypedStrData( aValStr, pMatVal->fVal, SC_STRTYPE_VALUE); + } + + if( rMatch < 0 && NULL != pCell && IsEqualToTokenArray( pCell, rPos, aCondTokArr ) ) + { + rMatch = n; + // short circuit on the first match if not filling the list + if( NULL == pStrings ) + return true; + } + + if( NULL != pEntry ) + { + lclInsertStringToCollection( *pStrings, pEntry, bSortList ); + n++; + } + } + } + + // In case of no match needed and an error occurred, return that error + // entry as valid instead of silently failing. + return bOk || NULL == pCell; +} + +bool ScValidationData::FillSelectionList( TypedScStrCollection& rStrColl, const ScAddress& rPos ) const +{ + bool bOk = false; + + if( HasSelectionList() ) + { + ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); + + // *** try if formula is a string list *** + + bool bSortList = (mnListType == ValidListType::SORTEDASCENDING); + UINT32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); + ScStringTokenIterator aIt( *pTokArr ); + for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) + { + double fValue; + bool bIsValue = GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ); + TypedStrData* pData = new TypedStrData( *pString, fValue, bIsValue ? SC_STRTYPE_VALUE : SC_STRTYPE_STANDARD ); + lclInsertStringToCollection( rStrColl, pData, bSortList ); + } + bOk = aIt.Ok(); + + // *** if not a string list, try if formula results in a cell range or + // anything else we recognize as valid *** + + if (!bOk) + { + int nMatch; + bOk = GetSelectionFromFormula( &rStrColl, NULL, rPos, *pTokArr, nMatch ); + } + } + + return bOk; +} + +// ---------------------------------------------------------------------------- + +bool ScValidationData::IsEqualToTokenArray( ScBaseCell* pCell, const ScAddress& rPos, const ScTokenArray& rTokArr ) const +{ + // create a condition entry that tests on equality and set the passed token array + ScConditionEntry aCondEntry( SC_COND_EQUAL, &rTokArr, NULL, GetDocument(), rPos ); + return aCondEntry.IsCellValid( pCell, rPos ); +} + +bool ScValidationData::IsListValid( ScBaseCell* pCell, const ScAddress& rPos ) const +{ + bool bIsValid = false; + + /* Compare input cell with all supported tokens from the formula. + Currently a formula may contain: + 1) A list of strings (at least one string). + 2) A single cell or range reference. + 3) A single defined name (must contain a cell/range reference, another + name, or DB range, or a formula resulting in a cell/range reference + or matrix/array). + 4) A single database range. + 5) A formula resulting in a cell/range reference or matrix/array. + */ + + ::std::auto_ptr< ScTokenArray > pTokArr( CreateTokenArry( 0 ) ); + + // *** try if formula is a string list *** + + UINT32 nFormat = lclGetCellFormat( *GetDocument(), rPos ); + ScStringTokenIterator aIt( *pTokArr ); + for( const String* pString = aIt.First(); pString && aIt.Ok(); pString = aIt.Next() ) + { + /* Do not break the loop, if a valid string has been found. + This is to find invalid tokens following in the formula. */ + if( !bIsValid ) + { + // create a formula containing a single string or number + ScTokenArray aCondTokArr; + double fValue; + if( GetDocument()->GetFormatTable()->IsNumberFormat( *pString, nFormat, fValue ) ) + aCondTokArr.AddDouble( fValue ); + else + aCondTokArr.AddString( *pString ); + + bIsValid = IsEqualToTokenArray( pCell, rPos, aCondTokArr ); + } + } + + if( !aIt.Ok() ) + bIsValid = false; + + // *** if not a string list, try if formula results in a cell range or + // anything else we recognize as valid *** + + if (!bIsValid) + { + int nMatch; + bIsValid = GetSelectionFromFormula( NULL, pCell, rPos, *pTokArr, nMatch ); + bIsValid = bIsValid && nMatch >= 0; + } + + return bIsValid; +} + +// ============================================================================ +// ============================================================================ + +ScValidationDataList::ScValidationDataList(const ScValidationDataList& rList) : + ScValidationEntries_Impl() +{ + // fuer Ref-Undo - echte Kopie mit neuen Tokens! + + USHORT nCount = rList.Count(); + + for (USHORT i=0; i<nCount; i++) + InsertNew( rList[i]->Clone() ); + + //! sortierte Eintraege aus rList schneller einfuegen ??? +} + +ScValidationDataList::ScValidationDataList(ScDocument* pNewDoc, + const ScValidationDataList& rList) +{ + // fuer neues Dokument - echte Kopie mit neuen Tokens! + + USHORT nCount = rList.Count(); + + for (USHORT i=0; i<nCount; i++) + InsertNew( rList[i]->Clone(pNewDoc) ); + + //! sortierte Eintraege aus rList schneller einfuegen ??? +} + +ScValidationData* ScValidationDataList::GetData( sal_uInt32 nKey ) +{ + //! binaer suchen + + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + if ((*this)[i]->GetKey() == nKey) + return (*this)[i]; + + DBG_ERROR("ScValidationDataList: Eintrag nicht gefunden"); + return NULL; +} + +void ScValidationDataList::CompileXML() +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->CompileXML(); +} + +void ScValidationDataList::UpdateReference( UpdateRefMode eUpdateRefMode, + const ScRange& rRange, SCsCOL nDx, SCsROW nDy, SCsTAB nDz ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->UpdateReference( eUpdateRefMode, rRange, nDx, nDy, nDz); +} + +void ScValidationDataList::UpdateMoveTab( SCTAB nOldPos, SCTAB nNewPos ) +{ + USHORT nCount = Count(); + for (USHORT i=0; i<nCount; i++) + (*this)[i]->UpdateMoveTab( nOldPos, nNewPos ); +} + +bool ScValidationDataList::MarkUsedExternalReferences() const +{ + bool bAllMarked = false; + USHORT nCount = Count(); + for (USHORT i=0; !bAllMarked && i<nCount; i++) + bAllMarked = (*this)[i]->MarkUsedExternalReferences(); + return bAllMarked; +} + +BOOL ScValidationDataList::operator==( const ScValidationDataList& r ) const +{ + // fuer Ref-Undo - interne Variablen werden nicht verglichen + + USHORT nCount = Count(); + BOOL bEqual = ( nCount == r.Count() ); + for (USHORT i=0; i<nCount && bEqual; i++) // Eintraege sind sortiert + if ( !(*this)[i]->EqualEntries(*r[i]) ) // Eintraege unterschiedlich ? + bEqual = FALSE; + + return bEqual; +} + |