diff options
Diffstat (limited to 'sc/source/core/data/table5.cxx')
-rw-r--r-- | sc/source/core/data/table5.cxx | 1295 |
1 files changed, 1295 insertions, 0 deletions
diff --git a/sc/source/core/data/table5.cxx b/sc/source/core/data/table5.cxx new file mode 100644 index 000000000..cddb28e6e --- /dev/null +++ b/sc/source/core/data/table5.cxx @@ -0,0 +1,1295 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed + * with this work for additional information regarding copyright + * ownership. The ASF licenses this file to you under the Apache + * License, Version 2.0 (the "License"); you may not use this file + * except in compliance with the License. You may obtain a copy of + * the License at http://www.apache.org/licenses/LICENSE-2.0 . + */ + +#include <scitems.hxx> +#include <attrib.hxx> +#include <formulacell.hxx> +#include <table.hxx> +#include <column.hxx> +#include <document.hxx> +#include <drwlayer.hxx> +#include <global.hxx> +#include <stlpool.hxx> +#include <tabprotection.hxx> +#include <globstr.hrc> +#include <scresid.hxx> +#include <segmenttree.hxx> +#include <columniterator.hxx> +#include <globalnames.hxx> +#include <scmod.hxx> +#include <printopt.hxx> +#include <bcaslot.hxx> +#include <compressedarray.hxx> +#include <userdat.hxx> + +#include <com/sun/star/sheet/TablePageBreakData.hpp> + +#include <osl/diagnose.h> + +#include <algorithm> +#include <limits> + +using ::com::sun::star::uno::Sequence; +using ::com::sun::star::sheet::TablePageBreakData; +using ::std::set; + +void ScTable::UpdatePageBreaks(const ScRange* pUserArea) +{ + if (rDocument.IsImportingXML()) + return; + + // pUserArea != NULL -> print area is specified. We need to force-update + // the page breaks. + + if (!pUserArea) + { + if (!bPageSizeValid) + return; + + // Always update breaks if force breaks option has changed + if (mbPageBreaksValid && mbForceBreaks == SC_MOD()->GetPrintOptions().GetForceBreaks()) + return; + } + + SfxStyleSheetBase* pStyle + = rDocument.GetStyleSheetPool()->Find(aPageStyle, SfxStyleFamily::Page); + if (!pStyle) + { + OSL_FAIL("UpdatePageBreaks: Style not found"); + return; + } + SfxItemSet* pStyleSet = &pStyle->GetItemSet(); + + SCCOL nStartCol = 0; + SCROW nStartRow = 0; + SCCOL nEndCol = rDocument.MaxCol(); + SCROW nEndRow = rDocument.MaxRow(); + if (pUserArea) + { + nStartCol = pUserArea->aStart.Col(); + nStartRow = pUserArea->aStart.Row(); + nEndCol = pUserArea->aEnd.Col(); + nEndRow = pUserArea->aEnd.Row(); + } + else + { + sal_uInt16 nAreaCount = GetPrintRangeCount(); + if (nAreaCount > 1) + { + // Show nothing, when multiple ranges + + for (SCCOL nX : GetColumnsRange(0, rDocument.MaxCol())) + RemoveColBreak(nX, true, false); + + RemoveRowPageBreaks(0, rDocument.MaxRow() - 1); + + 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(); + } + } // otherwise show everything + } + + // get bSkipColBreaks/bSkipRowBreaks flags: + // fdo#40788 - print range scale settings can cause manual breaks to be + // ignored (see below). This behaviour may now be set by the user. + mbForceBreaks = SC_MOD()->GetPrintOptions().GetForceBreaks(); + bool bSkipColBreaks = false; + bool bSkipRowBreaks = false; + + if (!mbForceBreaks) + { + if (const SfxUInt16Item* pItem = pStyleSet->GetItemIfSet(ATTR_PAGE_SCALETOPAGES, false)) + { + bSkipColBreaks = bSkipRowBreaks = pItem->GetValue() > 0; + } + + const ScPageScaleToItem* pScaleToItem; + if (!bSkipColBreaks && (pScaleToItem = pStyleSet->GetItemIfSet(ATTR_PAGE_SCALETO, false))) + { + // #i54993# when fitting to width or height, ignore only manual breaks in that direction + if (pScaleToItem->GetWidth() > 0) + bSkipColBreaks = true; + if (pScaleToItem->GetHeight() > 0) + bSkipRowBreaks = true; + } + } + + tools::Long nPageSizeX = aPageSizeTwips.Width(); + tools::Long nPageSizeY = aPageSizeTwips.Height(); + + // Beginning: Remove breaks + + for (SCCOL nX : GetColumnsRange(0, nStartCol - 1)) + RemoveColBreak(nX, true, false); + RemoveRowPageBreaks(0, nStartRow - 1); + + if (nStartCol > 0) + SetColBreak(nStartCol, true, false); // AREABREAK + if (nStartRow > 0) + SetRowBreak(nStartRow, true, false); // AREABREAK + + // Middle part: Distribute breaks + + bool bRepeatCol = (nRepeatStartX != SCCOL_REPEAT_NONE); + bool bColFound = false; + tools::Long nSizeX = 0; + for (SCCOL nX = nStartCol; nX <= nEndCol; nX++) + { + bool bStartOfPage = false; + tools::Long nThisX = ColHidden(nX) ? 0 : mpColWidth->GetValue(nX); + bool bManualBreak = HasColManualBreak(nX); + if ((nSizeX + nThisX > nPageSizeX) || (bManualBreak && !bSkipColBreaks)) + { + SetColBreak(nX, true, false); + nSizeX = 0; + bStartOfPage = true; + } + else if (nX != nStartCol) + RemoveColBreak(nX, true, false); + 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 -= ColHidden(i) ? 0 : mpColWidth->GetValue(i); + while (nX <= nRepeatEndX) + RemoveColBreak(++nX, true, false); + bColFound = true; + } + + nSizeX += nThisX; + } + + // Remove all page breaks in range. + RemoveRowPageBreaks(nStartRow + 1, nEndRow); + + // And set new page breaks. + bool bRepeatRow = (nRepeatStartY != SCROW_REPEAT_NONE); + bool bRowFound = false; + tools::Long nSizeY = 0; + ScFlatBoolRowSegments::ForwardIterator aIterHidden(*mpHiddenRows); + ScFlatUInt16RowSegments::ForwardIterator aIterHeights(*mpRowHeights); + SCROW nNextManualBreak = GetNextManualBreak(nStartRow); // -1 => no more manual breaks + for (SCROW nY = nStartRow; nY <= nEndRow; ++nY) + { + bool bStartOfPage = false; + bool bThisRowHidden = false; + const bool bHasValue = aIterHidden.getValue(nY, bThisRowHidden); + assert(bHasValue); + (void)bHasValue; + tools::Long nThisY = 0; + if (!bThisRowHidden) + { + sal_uInt16 nTmp; + const bool bHasHeight = aIterHeights.getValue(nY, nTmp); + assert(bHasHeight); + if (bHasHeight) + nThisY = static_cast<tools::Long>(nTmp); + } + + bool bManualBreak = false; + if (nNextManualBreak >= 0) + { + bManualBreak = (nY == nNextManualBreak); + if (nY >= nNextManualBreak) + // Query the next manual break position. + nNextManualBreak = GetNextManualBreak(nY + 1); + } + + if ((nSizeY + nThisY > nPageSizeY) || (bManualBreak && !bSkipRowBreaks)) + { + SetRowBreak(nY, true, false); + 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 + tools::Long nHeights = GetTotalRowHeight(nRepeatStartY, nRepeatEndY); +#if OSL_DEBUG_LEVEL > 0 + if (nHeights == ::std::numeric_limits<tools::Long>::max()) + OSL_FAIL("ScTable::UpdatePageBreaks: row heights overflow"); +#endif + nPageSizeY -= nHeights; + if (nY <= nRepeatEndY) + RemoveRowPageBreaks(nY, nRepeatEndY); + bRowFound = true; + } + + if (bThisRowHidden) + { + // Hidden row range. Skip them unless there is a manual break. + SCROW nLastCommon = aIterHidden.getLastPos(); + if (nNextManualBreak >= 0) + nLastCommon = ::std::min(nLastCommon, nNextManualBreak - 1); + nY = nLastCommon; + } + else + { + // Visible row range. + + SCROW nLastHidden = aIterHidden.getLastPos(); + SCROW nLastHeight = aIterHeights.getLastPos(); + SCROW nLastCommon = ::std::min(nLastHidden, nLastHeight); + if (nNextManualBreak >= 0) + nLastCommon = ::std::min(nLastCommon, nNextManualBreak - 1); + + if (nLastCommon > nY) + { + tools::Long nMaxMultiple = static_cast<tools::Long>(nLastCommon - nY); + tools::Long nMultiple = (nPageSizeY - nSizeY) / nThisY; + if (nMultiple > nMaxMultiple) + nMultiple = nMaxMultiple; + if (nMultiple > 1) + { + nSizeY += nThisY * (nMultiple - 1); + nY += nMultiple - 1; + } + } + } + + nSizeY += nThisY; + } + + // End: Remove Break + + if (nEndCol < rDocument.MaxCol()) + { + SetColBreak(nEndCol + 1, true, false); // AREABREAK + for (SCCOL nCol : GetColumnsRange(nEndCol + 2, rDocument.MaxCol())) + RemoveColBreak(nCol, true, false); + } + if (nEndRow < rDocument.MaxRow()) + { + SetRowBreak(nEndRow + 1, true, false); // AREABREAK + if (nEndRow + 2 <= rDocument.MaxRow()) + RemoveRowPageBreaks(nEndRow + 2, rDocument.MaxRow()); + } + mbPageBreaksValid + = !pUserArea; // #i116881# the valid flag can only apply to the "no user area" case +} + +void ScTable::RemoveManualBreaks() +{ + maRowManualBreaks.clear(); + maColManualBreaks.clear(); + InvalidatePageBreaks(); + + SetStreamValid(false); +} + +bool ScTable::HasManualBreaks() const +{ + return !maRowManualBreaks.empty() || !maColManualBreaks.empty(); +} + +void ScTable::SetRowManualBreaks(::std::set<SCROW>&& rBreaks) +{ + maRowManualBreaks = std::move(rBreaks); + InvalidatePageBreaks(); + SetStreamValid(false); +} + +void ScTable::SetColManualBreaks(::std::set<SCCOL>&& rBreaks) +{ + maColManualBreaks = std::move(rBreaks); + InvalidatePageBreaks(); + SetStreamValid(false); +} + +void ScTable::GetAllRowBreaks(set<SCROW>& rBreaks, bool bPage, bool bManual) const +{ + if (bPage) + rBreaks = maRowPageBreaks; + + if (bManual) + { + copy(maRowManualBreaks.begin(), maRowManualBreaks.end(), + inserter(rBreaks, rBreaks.begin())); + } +} + +void ScTable::GetAllColBreaks(set<SCCOL>& rBreaks, bool bPage, bool bManual) const +{ + if (bPage) + rBreaks = maColPageBreaks; + + if (bManual) + { + copy(maColManualBreaks.begin(), maColManualBreaks.end(), + inserter(rBreaks, rBreaks.begin())); + } +} + +bool ScTable::HasRowPageBreak(SCROW nRow) const +{ + if (!ValidRow(nRow)) + return false; + + return maRowPageBreaks.find(nRow) != maRowPageBreaks.end(); +} + +bool ScTable::HasColPageBreak(SCCOL nCol) const +{ + if (!ValidCol(nCol)) + return false; + + return maColPageBreaks.find(nCol) != maColPageBreaks.end(); +} + +bool ScTable::HasRowManualBreak(SCROW nRow) const +{ + if (!ValidRow(nRow)) + return false; + + return maRowManualBreaks.find(nRow) != maRowManualBreaks.end(); +} + +bool ScTable::HasColManualBreak(SCCOL nCol) const +{ + if (!ValidCol(nCol)) + return false; + + return maColManualBreaks.find(nCol) != maColManualBreaks.end(); +} + +SCROW ScTable::GetNextManualBreak(SCROW nRow) const +{ + set<SCROW>::const_iterator itr = maRowManualBreaks.lower_bound(nRow); + return itr == maRowManualBreaks.end() ? -1 : *itr; +} + +void ScTable::RemoveRowPageBreaks(SCROW nStartRow, SCROW nEndRow) +{ + if (!ValidRow(nStartRow) || !ValidRow(nEndRow)) + return; + + set<SCROW>::iterator low = maRowPageBreaks.lower_bound(nStartRow); + set<SCROW>::iterator high = maRowPageBreaks.upper_bound(nEndRow); + maRowPageBreaks.erase(low, high); +} + +void ScTable::RemoveRowBreak(SCROW nRow, bool bPage, bool bManual) +{ + if (!ValidRow(nRow)) + return; + + if (bPage) + maRowPageBreaks.erase(nRow); + + if (bManual) + { + maRowManualBreaks.erase(nRow); + InvalidatePageBreaks(); + } +} + +void ScTable::RemoveColBreak(SCCOL nCol, bool bPage, bool bManual) +{ + if (!ValidCol(nCol)) + return; + + if (bPage) + maColPageBreaks.erase(nCol); + + if (bManual) + { + maColManualBreaks.erase(nCol); + InvalidatePageBreaks(); + } +} + +void ScTable::SetRowBreak(SCROW nRow, bool bPage, bool bManual) +{ + if (!ValidRow(nRow)) + return; + + if (bPage) + maRowPageBreaks.insert(nRow); + + if (bManual) + { + maRowManualBreaks.insert(nRow); + InvalidatePageBreaks(); + } +} + +void ScTable::SetColBreak(SCCOL nCol, bool bPage, bool bManual) +{ + if (!ValidCol(nCol)) + return; + + if (bPage) + maColPageBreaks.insert(nCol); + + if (bManual) + { + maColManualBreaks.insert(nCol); + InvalidatePageBreaks(); + } +} + +Sequence<TablePageBreakData> ScTable::GetRowBreakData() const +{ + using ::std::inserter; + + set<SCROW> aRowBreaks = maRowPageBreaks; + copy(maRowManualBreaks.begin(), maRowManualBreaks.end(), + inserter(aRowBreaks, aRowBreaks.begin())); + + Sequence<TablePageBreakData> aSeq(aRowBreaks.size()); + std::transform(aRowBreaks.begin(), aRowBreaks.end(), aSeq.getArray(), [this](const SCROW nRow) { + return TablePageBreakData(nRow, HasRowManualBreak(nRow)); + }); + + return aSeq; +} + +bool ScTable::RowHidden(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const +{ + if (!ValidRow(nRow)) + { + if (pFirstRow) + *pFirstRow = nRow; + if (pLastRow) + *pLastRow = nRow; + return true; + } + + ScFlatBoolRowSegments::RangeData aData; + if (!mpHiddenRows->getRangeData(nRow, aData)) + { + // search failed. + if (pFirstRow) + *pFirstRow = nRow; + if (pLastRow) + *pLastRow = nRow; + return true; + } + + if (pFirstRow) + *pFirstRow = aData.mnRow1; + if (pLastRow) + *pLastRow = aData.mnRow2; + + return aData.mbValue; +} + +bool ScTable::RowHiddenLeaf(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const +{ + if (!ValidRow(nRow)) + { + if (pFirstRow) + *pFirstRow = nRow; + if (pLastRow) + *pLastRow = nRow; + return true; + } + + ScFlatBoolRowSegments::RangeData aData; + if (!mpHiddenRows->getRangeDataLeaf(nRow, aData)) + { + // search failed. + if (pFirstRow) + *pFirstRow = nRow; + if (pLastRow) + *pLastRow = nRow; + return true; + } + + if (pFirstRow) + *pFirstRow = aData.mnRow1; + if (pLastRow) + *pLastRow = aData.mnRow2; + + return aData.mbValue; +} + +bool ScTable::HasHiddenRows(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nStartRow; + while (nRow <= nEndRow) + { + SCROW nLastRow = -1; + bool bHidden = RowHidden(nRow, nullptr, &nLastRow); + if (bHidden) + return true; + + nRow = nLastRow + 1; + } + return false; +} + +bool ScTable::ColHidden(SCCOL nCol, SCCOL* pFirstCol, SCCOL* pLastCol) const +{ + if (!ValidCol(nCol)) + return true; + + ScFlatBoolColSegments::RangeData aData; + if (!mpHiddenCols->getRangeData(nCol, aData)) + return true; + + if (pFirstCol) + *pFirstCol = aData.mnCol1; + if (pLastCol) + *pLastCol = aData.mnCol2; + + return aData.mbValue; +} + +bool ScTable::SetRowHidden(SCROW nStartRow, SCROW nEndRow, bool bHidden) +{ + bool bChanged = false; + if (bHidden) + bChanged = mpHiddenRows->setTrue(nStartRow, nEndRow); + else + bChanged = mpHiddenRows->setFalse(nStartRow, nEndRow); + + // Cell anchored objects might change visibility + ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer(); + if (pDrawLayer) + { + std::vector<SdrObject*> aRowDrawObjects; + aRowDrawObjects = pDrawLayer->GetObjectsAnchoredToRows(GetTab(), nStartRow, nEndRow); + for (auto aObj : aRowDrawObjects) + { + ScDrawObjData* pData = ScDrawLayer::GetObjData(aObj); + if (pData) + { + if (bHidden) + aObj->SetVisible(false); + else if (!GetDoc().ColHidden(pData->maStart.Col(), pData->maStart.Tab())) + { + // Only change visibility if object is not hidden by a hidden col + aObj->SetVisible(true); + } + } + } + } + + if (bChanged) + { + SetStreamValid(false); + + { // Scoped bulk broadcast. + // Only subtotal formula cells will accept the notification of + // SfxHintId::ScHiddenRowsChanged, leaving the bulk will track + // those and broadcast SfxHintId::ScDataChanged to notify all + // dependents. + ScBulkBroadcast aBulkBroadcast(rDocument.GetBASM(), SfxHintId::ScDataChanged); + for (SCCOL i = 0; i < aCol.size(); i++) + { + aCol[i].BroadcastRows(nStartRow, nEndRow, SfxHintId::ScHiddenRowsChanged); + } + } + } + + return bChanged; +} + +void ScTable::SetColHidden(SCCOL nStartCol, SCCOL nEndCol, bool bHidden) +{ + bool bChanged = false; + if (bHidden) + bChanged = mpHiddenCols->setTrue(nStartCol, nEndCol); + else + bChanged = mpHiddenCols->setFalse(nStartCol, nEndCol); + + // Cell anchored objects might change visibility + ScDrawLayer* pDrawLayer = rDocument.GetDrawLayer(); + if (pDrawLayer) + { + std::vector<SdrObject*> aColDrawObjects; + aColDrawObjects = pDrawLayer->GetObjectsAnchoredToCols(GetTab(), nStartCol, nEndCol); + for (auto aObj : aColDrawObjects) + { + ScDrawObjData* pData = ScDrawLayer::GetObjData(aObj); + if (pData) + { + if (bHidden) + aObj->SetVisible(false); + else if (!GetDoc().RowHidden(pData->maStart.Row(), pData->maStart.Tab())) + { + // Only change visibility if object is not hidden by a hidden row + aObj->SetVisible(true); + } + } + } + } + + if (bChanged) + SetStreamValid(false); +} + +void ScTable::CopyColHidden(const ScTable& rTable, SCCOL nStartCol, SCCOL nEndCol) +{ + SCCOL nCol = nStartCol; + while (nCol <= nEndCol) + { + SCCOL nLastCol = -1; + bool bHidden = rTable.ColHidden(nCol, nullptr, &nLastCol); + if (nLastCol > nEndCol) + nLastCol = nEndCol; + + SetColHidden(nCol, nLastCol, bHidden); + nCol = nLastCol + 1; + } +} + +void ScTable::CopyRowHidden(const ScTable& rTable, SCROW nStartRow, SCROW nEndRow) +{ + SCROW nRow = nStartRow; + while (nRow <= nEndRow) + { + SCROW nLastRow = -1; + bool bHidden = rTable.RowHidden(nRow, nullptr, &nLastRow); + if (nLastRow > nEndRow) + nLastRow = nEndRow; + SetRowHidden(nRow, nLastRow, bHidden); + nRow = nLastRow + 1; + } +} + +void ScTable::CopyRowHeight(const ScTable& rSrcTable, SCROW nStartRow, SCROW nEndRow, + SCROW nSrcOffset) +{ + SCROW nRow = nStartRow; + ScFlatUInt16RowSegments::RangeData aSrcData; + while (nRow <= nEndRow) + { + if (!rSrcTable.mpRowHeights->getRangeData(nRow + nSrcOffset, aSrcData)) + // Something is wrong ! + return; + + SCROW nLastRow = aSrcData.mnRow2 - nSrcOffset; + if (nLastRow > nEndRow) + nLastRow = nEndRow; + + mpRowHeights->setValue(nRow, nLastRow, aSrcData.mnValue); + nRow = nLastRow + 1; + } +} + +SCROW ScTable::FirstVisibleRow(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nStartRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= nEndRow) + { + if (!ValidRow(nRow)) + break; + + if (!mpHiddenRows->getRangeData(nRow, aData)) + // failed to get range data. + break; + + if (!aData.mbValue) + // visible row found + return nRow; + + nRow = aData.mnRow2 + 1; + } + + return ::std::numeric_limits<SCROW>::max(); +} + +SCROW ScTable::LastVisibleRow(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nEndRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow >= nStartRow) + { + if (!ValidRow(nRow)) + break; + + if (!mpHiddenRows->getRangeData(nRow, aData)) + // failed to get range data. + break; + + if (!aData.mbValue) + // visible row found + return nRow; + + nRow = aData.mnRow1 - 1; + } + + return ::std::numeric_limits<SCROW>::max(); +} + +SCROW ScTable::CountVisibleRows(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nCount = 0; + SCROW nRow = nStartRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= nEndRow) + { + if (!mpHiddenRows->getRangeData(nRow, aData)) + break; + + if (aData.mnRow2 > nEndRow) + aData.mnRow2 = nEndRow; + + if (!aData.mbValue) + nCount += aData.mnRow2 - nRow + 1; + + nRow = aData.mnRow2 + 1; + } + return nCount; +} + +tools::Long ScTable::GetTotalRowHeight(SCROW nStartRow, SCROW nEndRow, bool bHiddenAsZero) const +{ + tools::Long nHeight = 0; + SCROW nRow = nStartRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= nEndRow) + { + if (!mpHiddenRows->getRangeData(nRow, aData)) + break; + + if (aData.mnRow2 > nEndRow) + aData.mnRow2 = nEndRow; + + if (!(bHiddenAsZero && aData.mbValue)) + // visible row range. + nHeight += mpRowHeights->getSumValue(nRow, aData.mnRow2); + + nRow = aData.mnRow2 + 1; + } + + return nHeight; +} + +SCCOL ScTable::CountVisibleCols(SCCOL nStartCol, SCCOL nEndCol) const +{ + assert(nStartCol <= nEndCol); + SCCOL nCount = 0; + SCCOL nCol = nStartCol; + ScFlatBoolColSegments::RangeData aData; + while (nCol <= nEndCol) + { + if (!mpHiddenCols->getRangeData(nCol, aData)) + break; + + if (aData.mnCol2 > nEndCol) + aData.mnCol2 = nEndCol; + + if (!aData.mbValue) + nCount += aData.mnCol2 - nCol + 1; + + nCol = aData.mnCol2 + 1; + } + return nCount; +} + +SCCOLROW ScTable::LastHiddenColRow(SCCOLROW nPos, bool bCol) const +{ + if (bCol) + { + SCCOL nCol = static_cast<SCCOL>(nPos); + if (ColHidden(nCol)) + { + for (SCCOL i = nCol + 1; i <= rDocument.MaxCol(); ++i) + { + if (!ColHidden(i)) + return i - 1; + } + } + } + else + { + SCROW nRow = static_cast<SCROW>(nPos); + SCROW nLastRow; + if (RowHidden(nRow, nullptr, &nLastRow)) + return static_cast<SCCOLROW>(nLastRow); + } + return ::std::numeric_limits<SCCOLROW>::max(); +} + +bool ScTable::RowFiltered(SCROW nRow, SCROW* pFirstRow, SCROW* pLastRow) const +{ + if (!ValidRow(nRow)) + return false; + + ScFlatBoolRowSegments::RangeData aData; + if (!mpFilteredRows->getRangeData(nRow, aData)) + // search failed. + return false; + + if (pFirstRow) + *pFirstRow = aData.mnRow1; + if (pLastRow) + *pLastRow = aData.mnRow2; + + return aData.mbValue; +} + +bool ScTable::ColFiltered(SCCOL nCol, SCCOL* pFirstCol, SCCOL* pLastCol) const +{ + if (!ValidCol(nCol)) + return false; + + ScFlatBoolColSegments::RangeData aData; + if (!mpFilteredCols->getRangeData(nCol, aData)) + // search failed. + return false; + + if (pFirstCol) + *pFirstCol = aData.mnCol1; + if (pLastCol) + *pLastCol = aData.mnCol2; + + return aData.mbValue; +} + +bool ScTable::HasFilteredRows(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nStartRow; + while (nRow <= nEndRow) + { + SCROW nLastRow = nRow; + bool bFiltered = RowFiltered(nRow, nullptr, &nLastRow); + if (bFiltered) + return true; + + nRow = nLastRow + 1; + } + return false; +} + +void ScTable::CopyColFiltered(const ScTable& rTable, SCCOL nStartCol, SCCOL nEndCol) +{ + SCCOL nCol = nStartCol; + while (nCol <= nEndCol) + { + SCCOL nLastCol = -1; + bool bFiltered = rTable.ColFiltered(nCol, nullptr, &nLastCol); + if (nLastCol > nEndCol) + nLastCol = nEndCol; + + SetColFiltered(nCol, nLastCol, bFiltered); + nCol = nLastCol + 1; + } +} + +void ScTable::CopyRowFiltered(const ScTable& rTable, SCROW nStartRow, SCROW nEndRow) +{ + SCROW nRow = nStartRow; + while (nRow <= nEndRow) + { + SCROW nLastRow = -1; + bool bFiltered = rTable.RowFiltered(nRow, nullptr, &nLastRow); + if (nLastRow > nEndRow) + nLastRow = nEndRow; + SetRowFiltered(nRow, nLastRow, bFiltered); + nRow = nLastRow + 1; + } +} + +void ScTable::SetRowFiltered(SCROW nStartRow, SCROW nEndRow, bool bFiltered) +{ + if (bFiltered) + mpFilteredRows->setTrue(nStartRow, nEndRow); + else + mpFilteredRows->setFalse(nStartRow, nEndRow); +} + +void ScTable::SetColFiltered(SCCOL nStartCol, SCCOL nEndCol, bool bFiltered) +{ + if (bFiltered) + mpFilteredCols->setTrue(nStartCol, nEndCol); + else + mpFilteredCols->setFalse(nStartCol, nEndCol); +} + +SCROW ScTable::FirstNonFilteredRow(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nStartRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= nEndRow) + { + if (!ValidRow(nRow)) + break; + + if (!mpFilteredRows->getRangeData(nRow, aData)) + // failed to get range data. + break; + + if (!aData.mbValue) + // non-filtered row found + return nRow; + + nRow = aData.mnRow2 + 1; + } + + return ::std::numeric_limits<SCROW>::max(); +} + +SCROW ScTable::LastNonFilteredRow(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nRow = nEndRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow >= nStartRow) + { + if (!ValidRow(nRow)) + break; + + if (!mpFilteredRows->getRangeData(nRow, aData)) + // failed to get range data. + break; + + if (!aData.mbValue) + // non-filtered row found + return nRow; + + nRow = aData.mnRow1 - 1; + } + + return ::std::numeric_limits<SCROW>::max(); +} + +SCROW ScTable::CountNonFilteredRows(SCROW nStartRow, SCROW nEndRow) const +{ + SCROW nCount = 0; + SCROW nRow = nStartRow; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= nEndRow) + { + if (!mpFilteredRows->getRangeData(nRow, aData)) + break; + + if (aData.mnRow2 > nEndRow) + aData.mnRow2 = nEndRow; + + if (!aData.mbValue) + nCount += aData.mnRow2 - nRow + 1; + + nRow = aData.mnRow2 + 1; + } + return nCount; +} + +bool ScTable::IsManualRowHeight(SCROW nRow) const +{ + return bool(pRowFlags->GetValue(nRow) & CRFlags::ManualSize); +} + +namespace +{ +void lcl_syncFlags(const ScDocument* pDocument, ScFlatBoolColSegments& rColSegments, + const ScFlatBoolRowSegments& rRowSegments, + ScBitMaskCompressedArray<SCCOL, CRFlags>* pColFlags, + ScBitMaskCompressedArray<SCROW, CRFlags>* pRowFlags, const CRFlags nFlagMask) +{ + using ::sal::static_int_cast; + + CRFlags nFlagMaskComplement = ~nFlagMask; + + pRowFlags->AndValue(0, pDocument->MaxRow(), nFlagMaskComplement); + pColFlags->AndValue(0, pDocument->MaxCol() + 1, nFlagMaskComplement); + + { + // row hidden flags. + + SCROW nRow = 0; + ScFlatBoolRowSegments::RangeData aData; + while (nRow <= pDocument->MaxRow()) + { + if (!rRowSegments.getRangeData(nRow, aData)) + break; + + if (aData.mbValue) + pRowFlags->OrValue(nRow, aData.mnRow2, nFlagMask); + + nRow = aData.mnRow2 + 1; + } + } + + { + // column hidden flags. + + SCCOL nCol = 0; + ScFlatBoolColSegments::RangeData aData; + while (nCol <= pDocument->MaxCol()) + { + if (!rColSegments.getRangeData(nCol, aData)) + break; + + if (aData.mbValue) + pColFlags->OrValue(nCol, aData.mnCol2, nFlagMask); + + nCol = aData.mnCol2 + 1; + } + } +} +} + +void ScTable::SyncColRowFlags() +{ + CRFlags nManualBreakComplement = ~CRFlags::ManualBreak; + + // Manual breaks. + pRowFlags->AndValue(0, rDocument.MaxRow(), nManualBreakComplement); + mpColFlags->AndValue(0, rDocument.MaxCol() + 1, nManualBreakComplement); + + for (const auto& rBreakPos : maRowManualBreaks) + pRowFlags->OrValue(rBreakPos, CRFlags::ManualBreak); + + for (const auto& rBreakPos : maColManualBreaks) + mpColFlags->OrValue(rBreakPos, CRFlags::ManualBreak); + + // Hidden flags. + lcl_syncFlags(&rDocument, *mpHiddenCols, *mpHiddenRows, mpColFlags.get(), pRowFlags.get(), + CRFlags::Hidden); + lcl_syncFlags(&rDocument, *mpFilteredCols, *mpFilteredRows, mpColFlags.get(), pRowFlags.get(), + CRFlags::Filtered); +} + +void ScTable::SetPageSize(const Size& rSize) +{ + if (!rSize.IsEmpty()) + { + if (aPageSizeTwips != rSize) + InvalidatePageBreaks(); + + bPageSizeValid = true; + aPageSizeTwips = rSize; + } + else + bPageSizeValid = false; +} + +bool ScTable::IsProtected() const { return pTabProtection && pTabProtection->isProtected(); } + +void ScTable::SetProtection(const ScTableProtection* pProtect) +{ + if (pProtect) + pTabProtection.reset(new ScTableProtection(*pProtect)); + else + pTabProtection.reset(); + + SetStreamValid(false); +} + +const ScTableProtection* ScTable::GetProtection() const { return pTabProtection.get(); } + +Size ScTable::GetPageSize() const +{ + if (bPageSizeValid) + return aPageSizeTwips; + else + return Size(); // blank +} + +void ScTable::SetRepeatArea(SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCROW nEndRow) +{ + // #i117952# page break calculation uses these values (set from ScPrintFunc), not pRepeatColRange/pRepeatRowRange + if (nStartCol != nRepeatStartX || nEndCol != nRepeatEndX || nStartRow != nRepeatStartY + || nEndRow != nRepeatEndY) + InvalidatePageBreaks(); + + nRepeatStartX = nStartCol; + nRepeatEndX = nEndCol; + nRepeatStartY = nStartRow; + nRepeatEndY = nEndRow; +} + +void ScTable::StartListening(const ScAddress& rAddress, SvtListener* pListener) +{ + if (!ValidCol(rAddress.Col())) + return; + + CreateColumnIfNotExists(rAddress.Col()).StartListening(*pListener, rAddress.Row()); +} + +void ScTable::EndListening(const ScAddress& rAddress, SvtListener* pListener) +{ + if (!ValidCol(rAddress.Col())) + return; + + if (rAddress.Col() < aCol.size()) + aCol[rAddress.Col()].EndListening(*pListener, rAddress.Row()); +} + +void ScTable::StartListening(sc::StartListeningContext& rCxt, const ScAddress& rAddress, + SvtListener& rListener) +{ + if (!ValidCol(rAddress.Col())) + return; + + CreateColumnIfNotExists(rAddress.Col()).StartListening(rCxt, rAddress, rListener); +} + +void ScTable::EndListening(sc::EndListeningContext& rCxt, const ScAddress& rAddress, + SvtListener& rListener) +{ + if (!ValidCol(rAddress.Col())) + return; + + if (rAddress.Col() < aCol.size()) + aCol[rAddress.Col()].EndListening(rCxt, rAddress, rListener); +} + +void ScTable::SetPageStyle(const OUString& rName) +{ + if (aPageStyle == rName) + return; + + OUString aStrNew = rName; + SfxStyleSheetBasePool* pStylePool = rDocument.GetStyleSheetPool(); + SfxStyleSheetBase* pNewStyle = pStylePool->Find(aStrNew, SfxStyleFamily::Page); + + if (!pNewStyle) + { + aStrNew = ScResId(STR_STYLENAME_STANDARD); + pNewStyle = pStylePool->Find(aStrNew, SfxStyleFamily::Page); + } + + if (aPageStyle == aStrNew) + return; + + SfxStyleSheetBase* pOldStyle = pStylePool->Find(aPageStyle, SfxStyleFamily::Page); + if (pOldStyle && pNewStyle) + { + SfxItemSet& rOldSet = pOldStyle->GetItemSet(); + SfxItemSet& rNewSet = pNewStyle->GetItemSet(); + auto getScaleValue = [](const SfxItemSet& rSet, sal_uInt16 nId) { + return static_cast<const SfxUInt16Item&>(rSet.Get(nId)).GetValue(); + }; + + const sal_uInt16 nOldScale = getScaleValue(rOldSet, ATTR_PAGE_SCALE); + const sal_uInt16 nOldScaleToPages = getScaleValue(rOldSet, ATTR_PAGE_SCALETOPAGES); + const sal_uInt16 nNewScale = getScaleValue(rNewSet, ATTR_PAGE_SCALE); + const sal_uInt16 nNewScaleToPages = getScaleValue(rNewSet, ATTR_PAGE_SCALETOPAGES); + + if ((nOldScale != nNewScale) || (nOldScaleToPages != nNewScaleToPages)) + InvalidateTextWidth(nullptr, nullptr, false, false); + } + + if (pNewStyle) // also without the old one (for UpdateStdNames) + aPageStyle = aStrNew; + + SetStreamValid(false); +} + +void ScTable::PageStyleModified(const OUString& rNewName) +{ + aPageStyle = rNewName; + InvalidateTextWidth(nullptr, nullptr, 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) + { + // Special case: only process the "from" cell. + SCCOL nCol = pAdrFrom->Col(); + SCROW nRow = pAdrFrom->Row(); + if (nCol >= aCol.size()) + return; + ScColumn& rCol = aCol[nCol]; + ScRefCellValue aCell = rCol.GetCellValue(nRow); + if (aCell.isEmpty()) + return; + + rCol.SetTextWidth(nRow, TEXTWIDTH_DIRTY); + + if (bNumFormatChanged) + rCol.SetScriptType(nRow, SvtScriptType::UNKNOWN); + + if (bBroadcast) + { // Only with CalcAsShown + switch (aCell.meType) + { + case CELLTYPE_VALUE: + rCol.Broadcast(nRow); + break; + case CELLTYPE_FORMULA: + aCell.mpFormula->SetDirty(); + break; + default: + { + // added to avoid warnings + } + } + } + + return; + } + + const SCCOL nCol1 = pAdrFrom ? pAdrFrom->Col() : 0; + const SCROW nRow1 = pAdrFrom ? pAdrFrom->Row() : 0; + const SCCOL nCol2 = pAdrTo ? pAdrTo->Col() : aCol.size() - 1; + const SCROW nRow2 = pAdrTo ? pAdrTo->Row() : rDocument.MaxRow(); + + for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol) + { + ScColumnTextWidthIterator aIter(GetDoc(), aCol[nCol], nRow1, nRow2); + sc::ColumnBlockPosition blockPos; // cache mdds position + InitColumnBlockPosition(blockPos, nCol); + + for (; aIter.hasCell(); aIter.next()) + { + SCROW nRow = aIter.getPos(); + aIter.setValue(TEXTWIDTH_DIRTY); + ScRefCellValue aCell = aCol[nCol].GetCellValue(blockPos, nRow); + if (aCell.isEmpty()) + continue; + + if (bNumFormatChanged) + aCol[nCol].SetScriptType(nRow, SvtScriptType::UNKNOWN); + + if (bBroadcast) + { // Only with CalcAsShown + switch (aCell.meType) + { + case CELLTYPE_VALUE: + aCol[nCol].Broadcast(nRow); + break; + case CELLTYPE_FORMULA: + aCell.mpFormula->SetDirty(); + break; + default: + { + // added to avoid warnings + } + } + } + } + } +} + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |