/* -*- 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 #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include 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(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::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(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&& rBreaks) { maRowManualBreaks = std::move(rBreaks); InvalidatePageBreaks(); SetStreamValid(false); } void ScTable::SetColManualBreaks(::std::set&& rBreaks) { maColManualBreaks = std::move(rBreaks); InvalidatePageBreaks(); SetStreamValid(false); } void ScTable::GetAllRowBreaks(set& 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& 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::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::iterator low = maRowPageBreaks.lower_bound(nStartRow); set::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 ScTable::GetRowBreakData() const { using ::std::inserter; set aRowBreaks = maRowPageBreaks; copy(maRowManualBreaks.begin(), maRowManualBreaks.end(), inserter(aRowBreaks, aRowBreaks.begin())); Sequence 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 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 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::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::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(nPos); if (ColHidden(nCol)) { for (SCCOL i = nCol + 1; i <= rDocument.MaxCol(); ++i) { if (!ColHidden(i)) return i - 1; } } } else { SCROW nRow = static_cast(nPos); SCROW nLastRow; if (RowHidden(nRow, nullptr, &nLastRow)) return static_cast(nLastRow); } return ::std::numeric_limits::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::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::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* pColFlags, ScBitMaskCompressedArray* 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(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: */