/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */ /* * 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 .
*/
tools::Long ScColumn::GetNeededSize(
SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY, const Fraction& rZoomX, const Fraction& rZoomY, bool bWidth, const ScNeededSizeOptions& rOptions, const ScPatternAttr** ppPatternChange, bool bInPrintTwips ) const
{ // If bInPrintTwips is set, the size calculated should be in print twips, // else it should be in pixels.
// Switch unit to MapTwip instead ? (temporarily and then revert before exit). if (bInPrintTwips)
assert(pDev->GetMapMode().GetMapUnit() == MapUnit::MapTwip);
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end() || it->type == sc::element_type_empty) // Empty cell, or invalid row. return 0;
//The pPattern may change in GetCondResult if (aCell.getType() == CELLTYPE_FORMULA)
{
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern;
} // line break?
// get "cell is value" flag // Must be synchronized with ScOutputData::LayoutStrings() bool bCellIsValue = (aCell.getType() == CELLTYPE_VALUE); if (aCell.getType() == CELLTYPE_FORMULA)
{
ScFormulaCell* pFCell = aCell.getFormula();
bCellIsValue = pFCell->IsRunning(); if (!bCellIsValue)
{
bCellIsValue = pFCell->IsValue(); if (bCellIsValue)
{ // the pattern may change in IsValue()
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern;
}
}
}
// #i111387#, tdf#121040: disable automatic line breaks for all number formats if (bBreak && bCellIsValue && (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER))
{ // If a formula cell needs to be interpreted during aCell.hasNumeric() // to determine the type, the pattern may get invalidated because the // result may set a number format. In which case there's also the // General format not set anymore... bool bMayInvalidatePattern = (aCell.getType() == CELLTYPE_FORMULA); const CellAttributeHolder aOldPattern(pPattern); bool bNumeric = aCell.hasNumeric(); if (bMayInvalidatePattern)
{
pPattern = pAttrArray->GetPattern( nRow ); if (ppPatternChange)
*ppPatternChange = pPattern; // XXX caller may have to check for change!
} if (bNumeric)
{ if (!bMayInvalidatePattern || ScPatternAttr::areSame(pPattern, aOldPattern.getScPatternAttr()))
bBreak = false; else
{
nFormat = pPattern->GetNumberFormat( rContext, pCondSet ); if (rContext.NFGetType(nFormat) == SvNumFormatType::NUMBER)
bBreak = false;
}
}
}
// get other attributes from pattern and conditional formatting
// also call SetFont for edit cells, because bGetFont may be set only once // bGetFont is set also if script type changes if (rOptions.bGetFont)
{
Fraction aFontZoom = ( eOrient == SvxCellOrientation::Standard ) ? rZoomX : rZoomY;
vcl::Font aFont;
aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern // font color doesn't matter here
pPattern->fillFontOnly(aFont, pDev, &aFontZoom, pCondSet, nScript);
pDev->SetFont(aFont);
}
Size aPaper( 1000000, 1000000 ); if ( eOrient==SvxCellOrientation::Stacked && !bAsianVertical )
aPaper.setWidth( 1 ); elseif (bBreak)
{ double fWidthFactor = bInPrintTwips ? 1.0 : nPPTX; if ( bTextWysiwyg )
{ // if text is formatted for printer, don't use PixelToLogic, // to ensure the exact same paper width (and same line breaks) as in // ScEditUtil::GetEditArea, used for output.
// tdf#59820 - search for the longest substring in a multiline string void checkLineBreak(const OUString& aStrVal)
{
sal_Int32 nFromIndex = 0;
sal_Int32 nToIndex = aStrVal.indexOf('\n', nFromIndex); // if there is no line break, just take the length of the entire string if (nToIndex == -1)
{
mnMaxLen = aStrVal.getLength();
maMaxLenStr = aStrVal;
} else
{
sal_Int32 nMaxLen = 0; // search for the longest substring in the multiline string while (nToIndex != -1)
{ if (nMaxLen < nToIndex - nFromIndex)
{
nMaxLen = nToIndex - nFromIndex;
}
nFromIndex = nToIndex + 1;
nToIndex = aStrVal.indexOf('\n', nFromIndex);
} // take into consideration the last part of multiline string
nToIndex = aStrVal.getLength() - nFromIndex; if (nMaxLen < nToIndex)
{
nMaxLen = nToIndex;
} // assign new maximum including its substring if (mnMaxLen < nMaxLen)
{
mnMaxLen = nMaxLen;
maMaxLenStr = aStrVal.subView(nFromIndex);
}
}
}
switch (rCell.getType())
{ case CELLTYPE_NONE: case CELLTYPE_VALUE:
mnMaxLen = aValStr.getLength();
maMaxLenStr = aValStr; break; case CELLTYPE_EDIT: case CELLTYPE_STRING: case CELLTYPE_FORMULA: default:
checkLineBreak(aValStr);
}
}
if ( pParam && pParam->mbSimpleText )
{ // all the same except for number format
SCROW nRow = 0; const ScPatternAttr* pPattern = GetPattern( nRow );
vcl::Font aFont;
aFont.SetKerning(FontKerning::NONE); // like ScDrawStringsVars::SetPattern // font color doesn't matter here
pPattern->fillFontOnly(aFont, pDev, &rZoomX);
pDev->SetFont(aFont); const SvxMarginItem* pMargin = &pPattern->GetItem(ATTR_MARGIN);
tools::Long nMargin = static_cast<tools::Long>( pMargin->GetLeftMargin() * nPPTX ) + static_cast<tools::Long>( pMargin->GetRightMargin() * nPPTX );
// Try to find the row that has the longest string, and measure the width of that string.
ScInterpreterContext& rContext = rDocument.GetNonThreadedContext();
sal_uInt32 nFormat = pPattern->GetNumberFormat(rContext); while ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0 && nRow <= 2)
{ // This is often used with CSV import or other data having a header // row; if there is no specific format set try next row for actual // data format. // Or again in case there was a leading sep=";" row or two header // rows.. const ScPatternAttr* pNextPattern = GetPattern( ++nRow ); if (!ScPatternAttr::areSame(pNextPattern, pPattern))
nFormat = pNextPattern->GetNumberFormat(rContext);
}
OUString aLongStr; const Color* pColor; if (pParam->mnMaxTextRow >= 0)
{
ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
aLongStr = ScCellFormat::GetString(
aCell, nFormat, &pColor, &rContext, rDocument);
} else
{ // Go though all non-empty cells within selection.
MaxStrLenFinder aFunc(rDocument, nFormat);
sc::CellStoreType::const_iterator itPos = maCells.begin(); for (constauto& rMarkedSpan : aMarkedSpans)
itPos = sc::ParseAllNonEmpty(itPos, maCells, rMarkedSpan.mnRow1, rMarkedSpan.mnRow2, aFunc);
if ( rPattern.GetItem(ATTR_FONT_EMPHASISMARK).GetEmphasisMark() != FontEmphasisMark::NONE )
{ // add height for emphasis marks //TODO: font metrics should be used instead
nHeight += nHeight / 4;
}
// with conditional formatting, always consider the individual cells
const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd); const sal_uInt16 nOptimalMinRowHeight = GetDoc().GetSheetOptimalMinRowHeight(nTab); while ( pPattern )
{ const ScMergeAttr* pMerge = &pPattern->GetItem(ATTR_MERGE); const ScMergeFlagAttr* pFlag = &pPattern->GetItem(ATTR_MERGE_FLAG); if ( pMerge->GetRowMerge() > 1 || pFlag->IsOverlapped() )
{ // do nothing - vertically with merged and overlapping, // horizontally only with overlapped (invisible) - // only one horizontal merged is always considered
} else
{ bool bStdAllowed = (pPattern->GetCellOrientation() == SvxCellOrientation::Standard); bool bStdOnly = false; if (bStdAllowed)
{ bool bBreak = pPattern->GetItem(ATTR_LINEBREAK).GetValue() ||
(pPattern->GetItem( ATTR_HOR_JUSTIFY ).GetValue() ==
SvxCellHorJustify::Block);
bStdOnly = !bBreak;
// conditional formatting: loop all cells if (bStdOnly &&
!pPattern->GetItem(ATTR_CONDITIONAL).GetCondFormatData().empty())
{
bStdOnly = false;
}
// rotated text: loop all cells if ( bStdOnly && pPattern->GetItem(ATTR_ROTATE_VALUE).GetValue() )
bStdOnly = false;
}
if (bStdOnly)
{ bool bHasEditCells = HasEditCells(nStart,nEnd,nEditPos); // Call to HasEditCells() may change pattern due to // calculation, => sync always. // We don't know which row changed first, but as pPattern // covered nStart to nEnd we can pick nStart. Worst case we // have to repeat that for every row in range if every row // changed.
pPattern = aIter.Resync( nStart, nStart, nEnd); if (bHasEditCells && nEnd < nEditPos)
bHasEditCells = false; // run into that again if (bHasEditCells) // includes mixed script types
{ if (nEditPos == nStart)
{
bStdOnly = false; if (nEnd > nEditPos)
nNextEnd = nEnd;
nEnd = nEditPos; // calculate single
bStdAllowed = false; // will be computed in any case per cell
} else
{
nNextEnd = nEnd;
nEnd = nEditPos - 1; // standard - part
}
}
}
// if everything below is already larger, the loop doesn't have to // be run again
SCROW nStdEnd = nEnd; if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
for (constauto& rSpan : aSpans)
{ for (SCROW nRow = rSpan.mnRow1; nRow <= rSpan.mnRow2; ++nRow)
{ // only calculate the cell height when it's used later (#37928#)
voidoperator() (size_t nRow, EditTextObject*& pObj)
{ // For the test on hard formatting (ScEditAttrTester), are the defaults in the // EditEngine of no importance. When the tester would later recognise the same // attributes in default and hard formatting and has to remove them, the correct // defaults must be set in the EditEngine for each cell.
// test for attributes if (!mpEngine)
{
mpEngine.reset(new ScFieldEditEngine(&mrDoc, mrDoc.GetEditPool())); // EEControlBits::ONLINESPELLING if there are errors already
mpEngine->SetControlWord(mpEngine->GetControlWord() | EEControlBits::ONLINESPELLING);
mrDoc.ApplyAsianEditSettings(*mpEngine);
}
mpEngine->SetTextCurrentDefaults(*pObj);
sal_Int32 nParCount = mpEngine->GetParagraphCount(); for (sal_Int32 nPar=0; nPar<nParCount; nPar++)
{
mpEngine->RemoveCharAttribs(nPar); const SfxItemSet& rOld = mpEngine->GetParaAttribs(nPar); if ( rOld.Count() )
{
SfxItemSet aNew( *rOld.GetPool(), rOld.GetRanges() ); // empty
mpEngine->SetParaAttribs( nPar, aNew );
}
} // change URL field to text (not possible otherwise, thus pType=0)
mpEngine->RemoveFields();
bool bSpellErrors = mpEngine->HasOnlineSpellErrors(); bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs // ScEditAttrTester is not needed anymore, arrays are gone
if (it->type != sc::element_type_empty) // Non-empty cell at the start position. returnfalse;
// start position of next block which is not empty.
SCROW nNextRow = nStartRow + it->size - aPos.second; return nEndRow < nNextRow;
}
SCSIZE ScColumn::GetEmptyLinesInBlock( SCROW nStartRow, SCROW nEndRow, ScDirection eDir ) const
{ // Given a range of rows, find a top or bottom empty segment. switch (eDir)
{ case DIR_TOP:
{ // Determine the length of empty head segment.
size_t nLength = nEndRow - nStartRow + 1;
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it->type != sc::element_type_empty) // First row is already not empty. return 0;
// length of this empty block minus the offset.
size_t nThisLen = it->size - aPos.second; return std::min(nThisLen, nLength);
} break; case DIR_BOTTOM:
{ // Determine the length of empty tail segment.
size_t nLength = nEndRow - nStartRow + 1;
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nEndRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it->type != sc::element_type_empty) // end row is already not empty. return 0;
// length of this empty block from the tip to the end row position.
size_t nThisLen = aPos.second + 1; return std::min(nThisLen, nLength);
} break; default:
;
}
return 0;
}
SCROW ScColumn::GetFirstDataPos() const
{ if (IsEmptyData()) return 0;
sc::CellStoreType::const_iterator it = maCells.begin(); if (it->type != sc::element_type_empty) return 0;
return it->size;
}
SCROW ScColumn::GetLastDataPos() const
{ if (IsEmptyData()) return 0;
sc::CellStoreType::const_reverse_iterator it = maCells.rbegin(); if (it->type != sc::element_type_empty) return GetDoc().MaxRow();
bool ScColumn::GetPrevDataPos(SCROW& rRow) const
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ if (it == maCells.begin()) // No more previous non-empty cell. returnfalse;
rRow -= aPos.second + 1; // Last row position of the previous block. returntrue;
}
// This block is not empty. if (aPos.second)
{ // There are preceding cells in this block. Simply move back one cell.
--rRow; returntrue;
}
// This is the first cell in a non-empty block. Move back to the previous block. if (it == maCells.begin()) // No more preceding block. returnfalse;
--rRow; // Move to the last cell of the previous block.
--it; if (it->type == sc::element_type_empty)
{ // This block is empty. if (it == maCells.begin()) // No more preceding blocks. returnfalse;
bool ScColumn::GetNextDataPos(SCROW& rRow) const// greater than rRow
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ // This block is empty. Skip ahead to the next block (if exists).
rRow += it->size - aPos.second;
++it; if (it == maCells.end()) // No more next block. returnfalse;
// Next block exists, and is non-empty. returntrue;
}
if (aPos.second < it->size - 1)
{ // There are still cells following the current position.
++rRow; returntrue;
}
// This is the last cell in the block. Move ahead to the next block.
rRow += it->size - aPos.second; // First cell in the next block.
++it; if (it == maCells.end()) // No more next block. returnfalse;
if (it->type == sc::element_type_empty)
{ // Next block is empty. Move to the next block.
rRow += it->size;
++it; if (it == maCells.end()) returnfalse;
}
// Trim down rRowStart first
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRowStart);
sc::CellStoreType::const_iterator it = aPos.first; if (it == maCells.end()) returnfalse;
if (it->type == sc::element_type_empty)
{ // This block is empty. Skip ahead to the next block (if exists).
nRowStartNew += it->size - aPos.second; if (nRowStartNew > rRowEnd) returnfalse;
++it; if (it == maCells.end()) // No more next block. returnfalse;
}
// Trim up rRowEnd next
aPos = maCells.position(rRowEnd);
it = aPos.first; if (it == maCells.end())
{
rRowStart = nRowStartNew; returntrue; // Because trimming of rRowStart is ok
}
if (it->type == sc::element_type_empty)
{ // rRowEnd cannot be in the first block which is empty !
assert(it != maCells.begin()); // This block is empty. Skip to the previous block (it exists).
nRowEndNew -= aPos.second + 1; // Last row position of the previous block.
assert(nRowStartNew <= nRowEndNew);
}
if (itPos->type != sc::element_type_empty) return nRow;
// Move to the first cell of the current empty block.
nRow -= aPos.second;
} while (nRow > 0);
return 0;
}
void ScColumn::CellStorageModified()
{ // Remove cached values. Given how often this function is called and how (not that) often // the cached values are used, it should be more efficient to just discard everything // instead of trying to figure out each time exactly what to discard.
GetDoc().DiscardFormulaGroupContext();
assert(sal::static_int_cast<SCROW>(maCells.size()) == GetDoc().GetMaxRowCount()
&& "Size of the cell array is incorrect." );
assert(sal::static_int_cast<SCROW>(maCellTextAttrs.size()) == GetDoc().GetMaxRowCount()
&& "Size of the cell text attribute array is incorrect.");
assert(sal::static_int_cast<SCROW>(maBroadcasters.size()) == GetDoc().GetMaxRowCount()
&& "Size of the broadcaster array is incorrect.");
#if DEBUG_COLUMN_STORAGE // Make sure that these two containers are synchronized wrt empty segments. auto lIsEmptyType = [](constauto& rElement) { return rElement.type == sc::element_type_empty; }; // Move to the first empty blocks. auto itCell = std::find_if(maCells.begin(), maCells.end(), lIsEmptyType); auto itAttr = std::find_if(maCellTextAttrs.begin(), maCellTextAttrs.end(), lIsEmptyType);
while (itCell != maCells.end())
{ if (itCell->position != itAttr->position || itCell->size != itAttr->size)
{
cout << "ScColumn::CellStorageModified: Cell array and cell text attribute array are out of sync." << endl;
cout << "-- cell array" << endl;
maCells.dump_blocks(cout);
cout << "-- attribute array" << endl;
maCellTextAttrs.dump_blocks(cout);
cout.flush();
abort();
}
// Move to the next empty blocks.
++itCell;
itCell = std::find_if(itCell, maCells.end(), lIsEmptyType);
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.