/************************************************************************* * * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * Copyright 2000, 2010 Oracle and/or its affiliates. * * OpenOffice.org - a multi-platform office productivity suite * * This file is part of OpenOffice.org. * * OpenOffice.org is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 * only, as published by the Free Software Foundation. * * OpenOffice.org is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License version 3 for more details * (a copy is included in the LICENSE file that accompanied this code). * * You should have received a copy of the GNU Lesser General Public License * version 3 along with OpenOffice.org. If not, see * * 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 #include #include #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" #include "sheetevents.hxx" #include "segmenttree.hxx" // ----------------------------------------------------------------------- ScTable::ScTable( ScDocument* pDoc, SCTAB nNewTab, const String& rNewName, BOOL bColInfo, BOOL bRowInfo ) : aName( rNewName ), aCodeName( 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 ), mpRowHeights( static_cast(NULL) ), pColFlags( NULL ), pRowFlags( NULL ), mpHiddenCols(new ScFlatBoolColSegments), mpHiddenRows(new ScFlatBoolRowSegments), mpFilteredCols(new ScFlatBoolColSegments), mpFilteredRows(new ScFlatBoolRowSegments), pOutlineTable( NULL ), pSheetEvents( NULL ), bTableAreaValid( FALSE ), bVisible( TRUE ), bStreamValid( FALSE ), bPendingRowHeights( FALSE ), bCalcNotification( 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 ), aTabBgColor( COL_AUTO ), nScenarioFlags( 0 ), bActiveScenario( FALSE ), mbPageBreaksValid(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) { mpRowHeights.reset(new ScFlatUInt16RowSegments(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(nTab), Size( nx, ny ), false ); } } 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 pRowFlags; delete pSheetEvents; 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 ) { 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; } const Color& ScTable::GetTabBgColor() const { return aTabBgColor; } void ScTable::SetTabBgColor(const Color& rColor) { if (aTabBgColor != rColor) { // The tab color has changed. Set this table 'modified'. aTabBgColor = rColor; if (IsStreamValid()) SetStreamValid(false); } } 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(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; nColSetState( nWeightedCount + nProgressStart ); } } } SCROW nRngStart = 0; SCROW nRngEnd = 0; USHORT nLast = 0; for (SCSIZE i=0; iGetValue( 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 ( nMinX0) --nStart; if (nEnd 0) if (!aCol[rStartCol-1].IsEmptyBlock(nStart,nEnd)) { --rStartCol; bChanged = TRUE; bLeft = 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; } } } 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; } } } 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; } } } bool ScTable::ShrinkToUsedDataArea( SCCOL& rStartCol, SCROW& rStartRow, SCCOL& rEndCol, SCROW& rEndRow, bool bColumnsOnly ) const { bool bRet = false; bool bChanged; do { bChanged = false; bool bCont = true; while (rEndCol > 0 && bCont && rStartCol < rEndCol) { if (aCol[rEndCol].IsEmptyBlock( rStartRow, rEndRow)) { --rEndCol; bChanged = true; } else bCont = false; } bCont = true; while (rStartCol < MAXCOL && bCont && rStartCol < rEndCol) { if (aCol[rStartCol].IsEmptyBlock( rStartRow, rEndRow)) { ++rStartCol; bChanged = true; } else bCont = false; } if (!bColumnsOnly) { if (rStartRow < MAXROW && rStartRow < rEndRow) { bool bFound = false; for (SCCOL i=rStartCol; i<=rEndCol && !bFound; i++) if (aCol[i].HasDataAt( rStartRow)) bFound = true; if (!bFound) { ++rStartRow; bChanged = true; } } if (rEndRow > 0 && rStartRow < rEndRow) { bool bFound = false; for (SCCOL i=rStartCol; i<=rEndCol && !bFound; i++) if (aCol[i].HasDataAt( rEndRow)) bFound = true; if (!bFound) { --rEndRow; bChanged = true; } } } if (bChanged) bRet = true; } while( bChanged ); return bRet; } 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(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( nNewCol + nMovX ); bFnd = (nNewCol>=0 && nNewCol<=MAXCOL) ? aCol[nNewCol].HasVisibleDataAt(rRow) : FALSE; } while (bFnd); nNewCol = sal::static_int_cast( nNewCol - nMovX ); if (nNewCol == (SCsCOL)rCol) bThere = FALSE; } if (!bThere) { do { nNewCol = sal::static_int_cast( 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 (RowHidden(nRow)) return FALSE; if (ColHidden(nCol)) 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( nCol + nMovX ); nRow = sal::static_int_cast( 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) && (RowHidden(nRow) || 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( nCol + static_cast(nMovY) ); while ( VALIDCOL(nCol) && ColHidden(nCol) ) nCol = sal::static_int_cast( nCol + static_cast(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) && (RowHidden(nRow) || 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 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, bool bUpdateNoteCaptionPos ) { 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( nCol1 - nDx ); nRow1 = sal::static_int_cast( nRow1 - nDy ); nCol2 = sal::static_int_cast( nCol2 - nDx ); nRow2 = sal::static_int_cast( nRow2 - nDy ); } pDrawLayer->MoveArea( nTab, nCol1,nRow1, nCol2,nRow2, nDx,nDy, (eUpdateRefMode == URM_INSDEL), bUpdateNoteCaptionPos ); } } } 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, bool bUpdateNoteCaptionPos ) { 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, bUpdateNoteCaptionPos ); 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& 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; // First, mark those columns that we need to skip i.e. hidden and empty columns. ScFlatBoolColSegments aSkipCols; aSkipCols.setInsertFromBack(true); // speed optimazation. aSkipCols.setFalse(0, MAXCOL); for (SCCOL i = 0; i <= MAXCOL; ++i) { SCCOL nLastCol = i; if (ColHidden(i, NULL, &nLastCol)) { // Columns are hidden in this range. aSkipCols.setTrue(i, nLastCol); } else { // These columns are visible. Check for empty columns. for (SCCOL j = i; j <= nLastCol; ++j) { if (aCol[j].GetCellCount() == 0) // empty aSkipCols.setTrue(j,j); } } i = nLastCol; } ScFlatBoolColSegments::RangeData aColData; for (SCCOL nCol = rEndCol; nCol >= 0; --nCol) { if (!aSkipCols.getRangeData(nCol, aColData)) // Failed to get the data. This should never happen! return; if (aColData.mbValue) { // Skip these columns. nCol = aColData.mnCol1; // move toward 0. continue; } // These are visible and non-empty columns. for (SCCOL nDataCol = nCol; 0 <= nDataCol && nDataCol >= aColData.mnCol1; --nDataCol) { SCCOL nPrintCol = nDataCol; VisibleDataCellIterator aIter(*mpHiddenRows, aCol[nDataCol]); ScBaseCell* pCell = aIter.reset(nStartRow); if (!pCell) // No visible cells found in this column. Skip it. continue; while (pCell) { SCCOL nNewCol = nDataCol; SCROW nRow = aIter.getRow(); if (nRow > nEndRow) // Went past the last row position. Bail out. break; MaybeAddExtraColumn(nNewCol, nRow, pDev, nPPTX, nPPTY); if (nNewCol > nPrintCol) nPrintCol = nNewCol; pCell = aIter.next(); } if (nPrintCol > rEndCol) // Make sure we don't shrink the print area. rEndCol = nPrintCol; } nCol = aColData.mnCol1; // move toward 0. } } void ScTable::MaybeAddExtraColumn(SCCOL& rCol, SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY) { ScBaseCell* pCell = aCol[rCol].GetCell(nRow); if (!pCell || !pCell->HasStringData()) return; bool bFormula = false; //! ueberge 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[rCol].GetNeededSize( nRow, pDev, nPPTX, nPPTY, aZoom, aZoom, true, aOptions ); pCell->SetTextWidth( (USHORT)nPixel ); } long nTwips = (long) (nPixel / nPPTX); long nDocW = GetColWidth( rCol ); long nMissing = nTwips - nDocW; if ( nMissing > 0 ) { // look at alignment const ScPatternAttr* pPattern = GetPattern( rCol, nRow ); const SfxItemSet* pCondSet = NULL; if ( ((const SfxUInt32Item&)pPattern->GetItem(ATTR_CONDITIONAL)).GetValue() ) pCondSet = pDocument->GetCondResult( rCol, 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 nNewCol = rCol; while (nMissing > 0 && nNewCol < MAXCOL) { ScBaseCell* pNextCell = aCol[nNewCol+1].GetCell(nRow); if (pNextCell && pNextCell->GetCellType() != CELLTYPE_NOTE) // Cell content in a next column ends display of this string. nMissing = 0; else nMissing -= GetColWidth(++nNewCol); } rCol = nNewCol; } 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); } SCROW ScTable::VisibleDataCellIterator::ROW_NOT_FOUND = -1; ScTable::VisibleDataCellIterator::VisibleDataCellIterator(ScFlatBoolRowSegments& rRowSegs, ScColumn& rColumn) : mrRowSegs(rRowSegs), mrColumn(rColumn), mpCell(NULL), mnCurRow(ROW_NOT_FOUND), mnUBound(ROW_NOT_FOUND) { } ScTable::VisibleDataCellIterator::~VisibleDataCellIterator() { } ScBaseCell* ScTable::VisibleDataCellIterator::reset(SCROW nRow) { if (nRow > MAXROW) { mnCurRow = ROW_NOT_FOUND; return NULL; } ScFlatBoolRowSegments::RangeData aData; if (!mrRowSegs.getRangeData(nRow, aData)) { mnCurRow = ROW_NOT_FOUND; return NULL; } if (!aData.mbValue) { // specified row is visible. Take it. mnCurRow = nRow; mnUBound = aData.mnRow2; } else { // specified row is not-visible. The first visible row is the start of // the next segment. mnCurRow = aData.mnRow2 + 1; mnUBound = mnCurRow; // get range data on the next iteration. if (mnCurRow > MAXROW) { // Make sure the row doesn't exceed our current limit. mnCurRow = ROW_NOT_FOUND; return NULL; } } mpCell = mrColumn.GetCell(mnCurRow); if (mpCell) // First visible cell found. return mpCell; // Find a first visible cell below this row (if any). return next(); } ScBaseCell* ScTable::VisibleDataCellIterator::next() { if (mnCurRow == ROW_NOT_FOUND) return NULL; while (mrColumn.GetNextDataPos(mnCurRow)) { if (mnCurRow > mnUBound) { // We don't know the visibility of this row range. Query it. ScFlatBoolRowSegments::RangeData aData; if (!mrRowSegs.getRangeData(mnCurRow, aData)) { mnCurRow = ROW_NOT_FOUND; return NULL; } if (aData.mbValue) { // This row is invisible. Skip to the last invisible row and // try again. mnCurRow = mnUBound = aData.mnRow2; continue; } // This row is visible. mnUBound = aData.mnRow2; } mpCell = mrColumn.GetCell(mnCurRow); if (mpCell) return mpCell; } mnCurRow = ROW_NOT_FOUND; return NULL; } SCROW ScTable::VisibleDataCellIterator::getRow() const { return mnCurRow; }