Files
loongoffice/sc/source/core/data/column2.cxx
Noel Grandin 229eed079c loplugin: cstylecast
Change-Id: If991c0efe1ded6ef6d32b3a722ee87bbe36bf0bf
2014-10-23 12:36:16 +02:00

3226 lines
107 KiB
C++

/* -*- 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 "column.hxx"
#include "scitems.hxx"
#include "formulacell.hxx"
#include "document.hxx"
#include "docpool.hxx"
#include "drwlayer.hxx"
#include "attarray.hxx"
#include "patattr.hxx"
#include "cellform.hxx"
#include "stlsheet.hxx"
#include "rechead.hxx"
#include "brdcst.hxx"
#include "editutil.hxx"
#include "subtotal.hxx"
#include "markdata.hxx"
#include "compiler.hxx"
#include "dbdata.hxx"
#include "fillinfo.hxx"
#include "segmenttree.hxx"
#include "docparam.hxx"
#include "cellvalue.hxx"
#include "tokenarray.hxx"
#include "globalnames.hxx"
#include "formulagroup.hxx"
#include "listenercontext.hxx"
#include "mtvcellfunc.hxx"
#include "scmatrix.hxx"
#include <rowheightcontext.hxx>
#include <math.h>
#include <editeng/eeitem.hxx>
#include <svx/algitem.hxx>
#include <editeng/editobj.hxx>
#include <editeng/editstat.hxx>
#include <editeng/emphasismarkitem.hxx>
#include <editeng/fhgtitem.hxx>
#include <editeng/forbiddencharacterstable.hxx>
#include <svx/rotmodit.hxx>
#include <editeng/scripttypeitem.hxx>
#include <editeng/unolingu.hxx>
#include <editeng/justifyitem.hxx>
#include <svl/zforlist.hxx>
#include <svl/broadcast.hxx>
#include <vcl/outdev.hxx>
#include <formula/errorcodes.hxx>
#include <formula/vectortoken.hxx>
#include <boost/scoped_ptr.hpp>
// factor from font size to optimal cell height (text width)
#define SC_ROT_BREAK_FACTOR 6
inline bool IsAmbiguousScript( sal_uInt8 nScript )
{
//! move to a header file
return ( nScript != SCRIPTTYPE_LATIN &&
nScript != SCRIPTTYPE_ASIAN &&
nScript != SCRIPTTYPE_COMPLEX );
}
// Data operations
long ScColumn::GetNeededSize(
SCROW nRow, OutputDevice* pDev, double nPPTX, double nPPTY,
const boost::rational<sal_Int64>& rZoomX, const boost::rational<sal_Int64>& rZoomY,
bool bWidth, const ScNeededSizeOptions& rOptions,
const ScPatternAttr** ppPatternChange ) const
{
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;
long nValue = 0;
ScRefCellValue aCell = GetCellValue(it, aPos.second);
double nPPT = bWidth ? nPPTX : nPPTY;
const ScPatternAttr* pPattern = rOptions.pPattern;
if (!pPattern)
pPattern = pAttrArray->GetPattern( nRow );
// merged?
// Do not merge in conditional formatting
const ScMergeAttr* pMerge = static_cast<const ScMergeAttr*>(&pPattern->GetItem(ATTR_MERGE));
const ScMergeFlagAttr* pFlag = static_cast<const ScMergeFlagAttr*>(&pPattern->GetItem(ATTR_MERGE_FLAG));
if ( bWidth )
{
if ( pFlag->IsHorOverlapped() )
return 0;
if ( rOptions.bSkipMerged && pMerge->GetColMerge() > 1 )
return 0;
}
else
{
if ( pFlag->IsVerOverlapped() )
return 0;
if ( rOptions.bSkipMerged && pMerge->GetRowMerge() > 1 )
return 0;
}
// conditional formatting
const SfxItemSet* pCondSet = pDocument->GetCondResult( nCol, nRow, nTab );
// line break?
const SfxPoolItem* pCondItem;
SvxCellHorJustify eHorJust;
if (pCondSet &&
pCondSet->GetItemState(ATTR_HOR_JUSTIFY, true, &pCondItem) == SfxItemState::SET)
eHorJust = (SvxCellHorJustify)static_cast<const SvxHorJustifyItem*>(pCondItem)->GetValue();
else
eHorJust = (SvxCellHorJustify)static_cast<const SvxHorJustifyItem&>(
pPattern->GetItem( ATTR_HOR_JUSTIFY )).GetValue();
bool bBreak;
if ( eHorJust == SVX_HOR_JUSTIFY_BLOCK )
bBreak = true;
else if ( pCondSet &&
pCondSet->GetItemState(ATTR_LINEBREAK, true, &pCondItem) == SfxItemState::SET)
bBreak = static_cast<const SfxBoolItem*>(pCondItem)->GetValue();
else
bBreak = static_cast<const SfxBoolItem&>(pPattern->GetItem(ATTR_LINEBREAK)).GetValue();
SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
sal_uLong nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
// #i111387# disable automatic line breaks only for "General" number format
if (bBreak && ( nFormat % SV_COUNTRY_LANGUAGE_OFFSET ) == 0 )
{
// 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.meType == CELLTYPE_FORMULA);
const ScPatternAttr* pOldPattern = 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 || pPattern == pOldPattern)
bBreak = false;
else
{
nFormat = pPattern->GetNumberFormat( pFormatter, pCondSet );
if ((nFormat % SV_COUNTRY_LANGUAGE_OFFSET) == 0)
bBreak = false;
}
}
}
// get other attributes from pattern and conditional formatting
SvxCellOrientation eOrient = pPattern->GetCellOrientation( pCondSet );
bool bAsianVertical = ( eOrient == SVX_ORIENTATION_STACKED &&
static_cast<const SfxBoolItem&>(pPattern->GetItem( ATTR_VERTICAL_ASIAN, pCondSet )).GetValue() );
if ( bAsianVertical )
bBreak = false;
if ( bWidth && bBreak ) // after determining bAsianVertical (bBreak may be reset)
return 0;
long nRotate = 0;
SvxRotateMode eRotMode = SVX_ROTATE_MODE_STANDARD;
if ( eOrient == SVX_ORIENTATION_STANDARD )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_ROTATE_VALUE, true, &pCondItem) == SfxItemState::SET)
nRotate = static_cast<const SfxInt32Item*>(pCondItem)->GetValue();
else
nRotate =static_cast<const SfxInt32Item&>(pPattern->GetItem(ATTR_ROTATE_VALUE)).GetValue();
if ( nRotate )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_ROTATE_MODE, true, &pCondItem) == SfxItemState::SET)
eRotMode = (SvxRotateMode)static_cast<const SvxRotateModeItem*>(pCondItem)->GetValue();
else
eRotMode = (SvxRotateMode)static_cast<const SvxRotateModeItem&>(
pPattern->GetItem(ATTR_ROTATE_MODE)).GetValue();
if ( nRotate == 18000 )
eRotMode = SVX_ROTATE_MODE_STANDARD; // no overflow
}
}
if ( eHorJust == SVX_HOR_JUSTIFY_REPEAT )
{
// ignore orientation/rotation if "repeat" is active
eOrient = SVX_ORIENTATION_STANDARD;
nRotate = 0;
bAsianVertical = false;
}
const SvxMarginItem* pMargin;
if (pCondSet &&
pCondSet->GetItemState(ATTR_MARGIN, true, &pCondItem) == SfxItemState::SET)
pMargin = static_cast<const SvxMarginItem*>(pCondItem);
else
pMargin = static_cast<const SvxMarginItem*>(&pPattern->GetItem(ATTR_MARGIN));
sal_uInt16 nIndent = 0;
if ( eHorJust == SVX_HOR_JUSTIFY_LEFT )
{
if (pCondSet &&
pCondSet->GetItemState(ATTR_INDENT, true, &pCondItem) == SfxItemState::SET)
nIndent = static_cast<const SfxUInt16Item*>(pCondItem)->GetValue();
else
nIndent = static_cast<const SfxUInt16Item&>(pPattern->GetItem(ATTR_INDENT)).GetValue();
}
sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab);
if (nScript == 0) nScript = ScGlobal::GetDefaultScriptType();
// also call SetFont for edit cells, because bGetFont may be set only once
// bGetFont is set also if script type changes
if (rOptions.bGetFont)
{
boost::rational<sal_Int64> aFontZoom = ( eOrient == SVX_ORIENTATION_STANDARD ) ? rZoomX : rZoomY;
vcl::Font aFont;
// font color doesn't matter here
pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &aFontZoom, pCondSet, nScript );
pDev->SetFont(aFont);
}
bool bAddMargin = true;
CellType eCellType = aCell.meType;
bool bEditEngine = (eCellType == CELLTYPE_EDIT ||
eOrient == SVX_ORIENTATION_STACKED ||
IsAmbiguousScript(nScript) ||
((eCellType == CELLTYPE_FORMULA) && aCell.mpFormula->IsMultilineResult()));
if (!bEditEngine) // direct output
{
Color* pColor;
OUString aValStr;
ScCellFormat::GetString(
aCell, nFormat, aValStr, &pColor, *pFormatter, pDocument, true, rOptions.bFormula, ftCheck);
if (!aValStr.isEmpty())
{
// SetFont is moved up
Size aSize( pDev->GetTextWidth( aValStr ), pDev->GetTextHeight() );
if ( eOrient != SVX_ORIENTATION_STANDARD )
{
long nTemp = aSize.Width();
aSize.Width() = aSize.Height();
aSize.Height() = nTemp;
}
else if ( nRotate )
{
//! take different X/Y scaling into consideration
double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad
double nCosAbs = fabs( cos( nRealOrient ) );
double nSinAbs = fabs( sin( nRealOrient ) );
long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
long nWidth;
if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
else if ( rOptions.bTotalSize )
{
nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT );
bAddMargin = false;
// only to the right:
//! differ on direction up/down (only Text/whole height)
if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT )
nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) *
nPPT * nCosAbs / nSinAbs );
}
else
nWidth = (long)( aSize.Height() / nSinAbs ); //! limit?
if ( bBreak && !rOptions.bTotalSize )
{
// limit size for line break
long nCmp = pDev->GetFont().GetSize().Height() * SC_ROT_BREAK_FACTOR;
if ( nHeight > nCmp )
nHeight = nCmp;
}
aSize = Size( nWidth, nHeight );
}
nValue = bWidth ? aSize.Width() : aSize.Height();
if ( bAddMargin )
{
if (bWidth)
{
nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
(long) ( pMargin->GetRightMargin() * nPPT );
if ( nIndent )
nValue += (long) ( nIndent * nPPT );
}
else
nValue += (long) ( pMargin->GetTopMargin() * nPPT ) +
(long) ( pMargin->GetBottomMargin() * nPPT );
}
// linebreak done ?
if ( bBreak && !bWidth )
{
// test with EditEngine the safety at 90%
// (due to rounding errors and because EditEngine formats partially differently)
long nDocPixel = (long) ( ( pDocument->GetColWidth( nCol,nTab ) -
pMargin->GetLeftMargin() - pMargin->GetRightMargin() -
nIndent )
* nPPT );
nDocPixel = (nDocPixel * 9) / 10; // for safety
if ( aSize.Width() > nDocPixel )
bEditEngine = true;
}
}
}
if (bEditEngine)
{
// the font is not reset each time with !bEditEngine
vcl::Font aOldFont = pDev->GetFont();
MapMode aHMMMode( MAP_100TH_MM, Point(), rZoomX, rZoomY );
// save in document ?
ScFieldEditEngine* pEngine = pDocument->CreateFieldEditEngine();
pEngine->SetUpdateMode( false );
bool bTextWysiwyg = ( pDev->GetOutDevType() == OUTDEV_PRINTER );
sal_uLong nCtrl = pEngine->GetControlWord();
if ( bTextWysiwyg )
nCtrl |= EE_CNTRL_FORMAT100;
else
nCtrl &= ~EE_CNTRL_FORMAT100;
pEngine->SetControlWord( nCtrl );
MapMode aOld = pDev->GetMapMode();
pDev->SetMapMode( aHMMMode );
pEngine->SetRefDevice( pDev );
pDocument->ApplyAsianEditSettings( *pEngine );
SfxItemSet* pSet = new SfxItemSet( pEngine->GetEmptyItemSet() );
if ( ScStyleSheet* pPreviewStyle = pDocument->GetPreviewCellStyle( nCol, nRow, nTab ) )
{
boost::scoped_ptr<ScPatternAttr> pPreviewPattern(new ScPatternAttr( *pPattern ));
pPreviewPattern->SetStyleSheet(pPreviewStyle);
pPreviewPattern->FillEditItemSet( pSet, pCondSet );
}
else
{
SfxItemSet* pFontSet = pDocument->GetPreviewFont( nCol, nRow, nTab );
pPattern->FillEditItemSet( pSet, pFontSet ? pFontSet : pCondSet );
}
// no longer needed, are setted with the text (is faster)
// pEngine->SetDefaults( pSet );
if ( static_cast<const SfxBoolItem&>(pSet->Get(EE_PARA_HYPHENATE)).GetValue() ) {
com::sun::star::uno::Reference<com::sun::star::linguistic2::XHyphenator> xXHyphenator( LinguMgr::GetHyphenator() );
pEngine->SetHyphenator( xXHyphenator );
}
Size aPaper = Size( 1000000, 1000000 );
if ( eOrient==SVX_ORIENTATION_STACKED && !bAsianVertical )
aPaper.Width() = 1;
else if (bBreak)
{
double fWidthFactor = nPPTX;
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.
fWidthFactor = HMM_PER_TWIPS;
}
// use original width for hidden columns:
long nDocWidth = (long) ( pDocument->GetOriginalWidth(nCol,nTab) * fWidthFactor );
SCCOL nColMerge = pMerge->GetColMerge();
if (nColMerge > 1)
for (SCCOL nColAdd=1; nColAdd<nColMerge; nColAdd++)
nDocWidth += (long) ( pDocument->GetColWidth(nCol+nColAdd,nTab) * fWidthFactor );
nDocWidth -= (long) ( pMargin->GetLeftMargin() * fWidthFactor )
+ (long) ( pMargin->GetRightMargin() * fWidthFactor )
+ 1; // output size is width-1 pixel (due to gridline)
if ( nIndent )
nDocWidth -= (long) ( nIndent * fWidthFactor );
// space for AutoFilter button: 20 * nZoom/100
if ( pFlag->HasAutoFilter() && !bTextWysiwyg )
nDocWidth -= (rZoomX.numerator()*20)/rZoomX.denominator();
aPaper.Width() = nDocWidth;
if ( !bTextWysiwyg )
aPaper = pDev->PixelToLogic( aPaper, aHMMMode );
}
pEngine->SetPaperSize(aPaper);
if (aCell.meType == CELLTYPE_EDIT)
{
pEngine->SetTextNewDefaults(*aCell.mpEditText, pSet);
}
else
{
Color* pColor;
OUString aString;
ScCellFormat::GetString(
aCell, nFormat, aString, &pColor, *pFormatter, pDocument, true,
rOptions.bFormula, ftCheck);
if (!aString.isEmpty())
pEngine->SetTextNewDefaults(aString, pSet);
else
pEngine->SetDefaults(pSet);
}
bool bEngineVertical = pEngine->IsVertical();
pEngine->SetVertical( bAsianVertical );
pEngine->SetUpdateMode( true );
bool bEdWidth = bWidth;
if ( eOrient != SVX_ORIENTATION_STANDARD && eOrient != SVX_ORIENTATION_STACKED )
bEdWidth = !bEdWidth;
if ( nRotate )
{
//! take different X/Y scaling into consideration
Size aSize( pEngine->CalcTextWidth(), pEngine->GetTextHeight() );
double nRealOrient = nRotate * F_PI18000; // nRotate is in 1/100 Grad
double nCosAbs = fabs( cos( nRealOrient ) );
double nSinAbs = fabs( sin( nRealOrient ) );
long nHeight = (long)( aSize.Height() * nCosAbs + aSize.Width() * nSinAbs );
long nWidth;
if ( eRotMode == SVX_ROTATE_MODE_STANDARD )
nWidth = (long)( aSize.Width() * nCosAbs + aSize.Height() * nSinAbs );
else if ( rOptions.bTotalSize )
{
nWidth = (long) ( pDocument->GetColWidth( nCol,nTab ) * nPPT );
bAddMargin = false;
if ( pPattern->GetRotateDir( pCondSet ) == SC_ROTDIR_RIGHT )
nWidth += (long)( pDocument->GetRowHeight( nRow,nTab ) *
nPPT * nCosAbs / nSinAbs );
}
else
nWidth = (long)( aSize.Height() / nSinAbs ); //! limit?
aSize = Size( nWidth, nHeight );
Size aPixSize = pDev->LogicToPixel( aSize, aHMMMode );
if ( bEdWidth )
nValue = aPixSize.Width();
else
{
nValue = aPixSize.Height();
if ( bBreak && !rOptions.bTotalSize )
{
// limit size for line break
long nCmp = aOldFont.GetSize().Height() * SC_ROT_BREAK_FACTOR;
if ( nValue > nCmp )
nValue = nCmp;
}
}
}
else if ( bEdWidth )
{
if (bBreak)
nValue = 0;
else
nValue = pDev->LogicToPixel(Size( pEngine->CalcTextWidth(), 0 ),
aHMMMode).Width();
}
else // height
{
nValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ),
aHMMMode).Height();
// With non-100% zoom and several lines or paragraphs, don't shrink below the result with FORMAT100 set
if ( !bTextWysiwyg && ( rZoomY.numerator() != 1 || rZoomY.denominator() != 1 ) &&
( pEngine->GetParagraphCount() > 1 || ( bBreak && pEngine->GetLineCount(0) > 1 ) ) )
{
pEngine->SetControlWord( nCtrl | EE_CNTRL_FORMAT100 );
pEngine->QuickFormatDoc( true );
long nSecondValue = pDev->LogicToPixel(Size( 0, pEngine->GetTextHeight() ), aHMMMode).Height();
if ( nSecondValue > nValue )
nValue = nSecondValue;
}
}
if ( nValue && bAddMargin )
{
if (bWidth)
{
nValue += (long) ( pMargin->GetLeftMargin() * nPPT ) +
(long) ( pMargin->GetRightMargin() * nPPT );
if (nIndent)
nValue += (long) ( nIndent * nPPT );
}
else
{
nValue += (long) ( pMargin->GetTopMargin() * nPPT ) +
(long) ( pMargin->GetBottomMargin() * nPPT );
if ( bAsianVertical && pDev->GetOutDevType() != OUTDEV_PRINTER )
{
// add 1pt extra (default margin value) for line breaks with SetVertical
nValue += (long) ( 20 * nPPT );
}
}
}
// EditEngine is cached and re-used, so the old vertical flag must be restored
pEngine->SetVertical( bEngineVertical );
pDocument->DisposeFieldEditEngine(pEngine);
pDev->SetMapMode( aOld );
pDev->SetFont( aOldFont );
}
if (bWidth)
{
// place for Autofilter Button
// 20 * nZoom/100
// Conditional formatting is not interesting here
sal_Int16 nFlags = static_cast<const ScMergeFlagAttr&>(pPattern->GetItem(ATTR_MERGE_FLAG)).GetValue();
if (nFlags & SC_MF_AUTO)
nValue += (rZoomX.numerator()*20)/rZoomX.denominator();
}
return nValue;
}
namespace {
class MaxStrLenFinder
{
ScDocument& mrDoc;
sal_uInt32 mnFormat;
OUString maMaxLenStr;
sal_Int32 mnMaxLen;
void checkLength(ScRefCellValue& rCell)
{
Color* pColor;
OUString aValStr;
ScCellFormat::GetString(
rCell, mnFormat, aValStr, &pColor, *mrDoc.GetFormatTable(), &mrDoc, true, false, ftCheck);
if (aValStr.getLength() > mnMaxLen)
{
mnMaxLen = aValStr.getLength();
maMaxLenStr = aValStr;
}
}
public:
MaxStrLenFinder(ScDocument& rDoc, sal_uInt32 nFormat) :
mrDoc(rDoc), mnFormat(nFormat), mnMaxLen(0) {}
void operator() (size_t /*nRow*/, double f)
{
ScRefCellValue aCell(f);
checkLength(aCell);
}
void operator() (size_t /*nRow*/, const svl::SharedString& rSS)
{
if (rSS.getLength() > mnMaxLen)
{
mnMaxLen = rSS.getLength();
maMaxLenStr = rSS.getString();
}
}
void operator() (size_t /*nRow*/, const EditTextObject* p)
{
ScRefCellValue aCell(p);
checkLength(aCell);
}
void operator() (size_t /*nRow*/, const ScFormulaCell* p)
{
ScRefCellValue aCell(const_cast<ScFormulaCell*>(p));
checkLength(aCell);
}
const OUString& getMaxLenStr() const { return maMaxLenStr; }
};
}
sal_uInt16 ScColumn::GetOptimalColWidth(
OutputDevice* pDev, double nPPTX, double nPPTY, const boost::rational<sal_Int64>& rZoomX, const boost::rational<sal_Int64>& rZoomY,
bool bFormula, sal_uInt16 nOldWidth, const ScMarkData* pMarkData, const ScColWidthParam* pParam) const
{
if (maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty)
// All cells are empty.
return nOldWidth;
sc::SingleColumnSpanSet aSpanSet;
sc::SingleColumnSpanSet::SpansType aMarkedSpans;
if (pMarkData && (pMarkData->IsMarked() || pMarkData->IsMultiMarked()))
{
aSpanSet.scan(*pMarkData, nTab, nCol);
aSpanSet.getSpans(aMarkedSpans);
}
else
// "Select" the entire column if no selection exists.
aMarkedSpans.push_back(sc::RowSpan(0, MAXROW));
sal_uInt16 nWidth = static_cast<sal_uInt16>(nOldWidth*nPPTX);
bool bFound = false;
if ( pParam && pParam->mbSimpleText )
{ // all the same except for number format
const ScPatternAttr* pPattern = GetPattern( 0 );
vcl::Font aFont;
// font color doesn't matter here
pPattern->GetFont( aFont, SC_AUTOCOL_BLACK, pDev, &rZoomX, NULL );
pDev->SetFont( aFont );
const SvxMarginItem* pMargin = static_cast<const SvxMarginItem*>(&pPattern->GetItem(ATTR_MARGIN));
long nMargin = (long) ( pMargin->GetLeftMargin() * nPPTX ) +
(long) ( pMargin->GetRightMargin() * nPPTX );
// Try to find the row that has the longest string, and measure the width of that string.
SvNumberFormatter* pFormatter = pDocument->GetFormatTable();
sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
OUString aLongStr;
Color* pColor;
if (pParam->mnMaxTextRow >= 0)
{
ScRefCellValue aCell = GetCellValue(pParam->mnMaxTextRow);
ScCellFormat::GetString(
aCell, nFormat, aLongStr, &pColor, *pFormatter, pDocument, true, false, ftCheck);
}
else
{
// Go though all non-empty cells within selection.
MaxStrLenFinder aFunc(*pDocument, nFormat);
sc::CellStoreType::const_iterator itPos = maCells.begin();
sc::SingleColumnSpanSet::SpansType::const_iterator it = aMarkedSpans.begin(), itEnd = aMarkedSpans.end();
for (; it != itEnd; ++it)
itPos = sc::ParseAllNonEmpty(itPos, maCells, it->mnRow1, it->mnRow2, aFunc);
aLongStr = aFunc.getMaxLenStr();
}
if (!aLongStr.isEmpty())
{
nWidth = pDev->GetTextWidth(aLongStr) + static_cast<sal_uInt16>(nMargin);
bFound = true;
}
}
else
{
ScNeededSizeOptions aOptions;
aOptions.bFormula = bFormula;
const ScPatternAttr* pOldPattern = NULL;
sal_uInt8 nOldScript = 0;
// Go though all non-empty cells within selection.
sc::CellStoreType::const_iterator itPos = maCells.begin();
sc::SingleColumnSpanSet::SpansType::const_iterator it = aMarkedSpans.begin(), itEnd = aMarkedSpans.end();
for (; it != itEnd; ++it)
{
SCROW nRow1 = it->mnRow1, nRow2 = it->mnRow2;
SCROW nRow = nRow1;
while (nRow <= nRow2)
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
itPos = aPos.first;
if (itPos->type == sc::element_type_empty)
{
// Skip empty cells.
nRow += itPos->size - aPos.second;
continue;
}
for (size_t nOffset = aPos.second; nOffset < itPos->size; ++nOffset, ++nRow)
{
sal_uInt8 nScript = pDocument->GetScriptType(nCol, nRow, nTab);
if (nScript == 0)
nScript = ScGlobal::GetDefaultScriptType();
const ScPatternAttr* pPattern = GetPattern(nRow);
aOptions.pPattern = pPattern;
aOptions.bGetFont = (pPattern != pOldPattern || nScript != nOldScript);
pOldPattern = pPattern;
sal_uInt16 nThis = (sal_uInt16) GetNeededSize(
nRow, pDev, nPPTX, nPPTY, rZoomX, rZoomY, true, aOptions, &pOldPattern);
if (nThis)
{
if (nThis > nWidth || !bFound)
{
nWidth = nThis;
bFound = true;
}
}
}
}
}
}
if (bFound)
{
nWidth += 2;
sal_uInt16 nTwips = (sal_uInt16) (nWidth / nPPTX);
return nTwips;
}
else
return nOldWidth;
}
static sal_uInt16 lcl_GetAttribHeight( const ScPatternAttr& rPattern, sal_uInt16 nFontHeightId )
{
const SvxFontHeightItem& rFontHeight =
static_cast<const SvxFontHeightItem&>(rPattern.GetItem(nFontHeightId));
sal_uInt16 nHeight = rFontHeight.GetHeight();
nHeight *= 1.18;
if ( static_cast<const SvxEmphasisMarkItem&>(rPattern.
GetItem(ATTR_FONT_EMPHASISMARK)).GetEmphasisMark() != EMPHASISMARK_NONE )
{
// add height for emphasis marks
//! font metrics should be used instead
nHeight += nHeight / 4;
}
const SvxMarginItem& rMargin =
static_cast<const SvxMarginItem&>(rPattern.GetItem(ATTR_MARGIN));
nHeight += rMargin.GetTopMargin() + rMargin.GetBottomMargin();
if (nHeight > STD_ROWHEIGHT_DIFF)
nHeight -= STD_ROWHEIGHT_DIFF;
if (nHeight < ScGlobal::nStdRowHeight)
nHeight = ScGlobal::nStdRowHeight;
return nHeight;
}
// pHeight in Twips
// optimize nMinHeight, nMinStart : with nRow >= nMinStart is at least nMinHeight
// (is only evaluated with bStdAllowed)
void ScColumn::GetOptimalHeight(
sc::RowHeightContext& rCxt, SCROW nStartRow, SCROW nEndRow, sal_uInt16 nMinHeight, SCROW nMinStart )
{
std::vector<sal_uInt16>& rHeights = rCxt.getHeightArray();
ScAttrIterator aIter( pAttrArray, nStartRow, nEndRow );
SCROW nStart = -1;
SCROW nEnd = -1;
SCROW nEditPos = 0;
SCROW nNextEnd = 0;
// with conditional formatting, always consider the individual cells
const ScPatternAttr* pPattern = aIter.Next(nStart,nEnd);
while ( pPattern )
{
const ScMergeAttr* pMerge = static_cast<const ScMergeAttr*>(&pPattern->GetItem(ATTR_MERGE));
const ScMergeFlagAttr* pFlag = static_cast<const ScMergeFlagAttr*>(&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() == SVX_ORIENTATION_STANDARD);
bool bStdOnly = false;
if (bStdAllowed)
{
bool bBreak = static_cast<const SfxBoolItem&>(pPattern->GetItem(ATTR_LINEBREAK)).GetValue() ||
((SvxCellHorJustify)static_cast<const SvxHorJustifyItem&>(pPattern->
GetItem( ATTR_HOR_JUSTIFY )).GetValue() ==
SVX_HOR_JUSTIFY_BLOCK);
bStdOnly = !bBreak;
// conditional formatting: loop all cells
if (bStdOnly &&
!static_cast<const ScCondFormatItem&>(pPattern->GetItem(
ATTR_CONDITIONAL)).GetCondFormatData().empty())
{
bStdOnly = false;
}
// rotated text: loop all cells
if ( bStdOnly && static_cast<const SfxInt32Item&>(pPattern->
GetItem(ATTR_ROTATE_VALUE)).GetValue() )
bStdOnly = false;
}
if (bStdOnly)
if (HasEditCells(nStart,nEnd,nEditPos)) // includes mixed script types
{
if (nEditPos == nStart)
{
bStdOnly = false;
if (nEnd > nEditPos)
nNextEnd = nEnd;
nEnd = nEditPos; // calculate single
bStdAllowed = false; // will be computed in any case per cell
}
else
{
nNextEnd = nEnd;
nEnd = nEditPos - 1; // standard - part
}
}
sc::SingleColumnSpanSet aSpanSet;
aSpanSet.scan(*this, nStart, nEnd);
sc::SingleColumnSpanSet::SpansType aSpans;
aSpanSet.getSpans(aSpans);
if (bStdAllowed)
{
sal_uInt16 nLatHeight = 0;
sal_uInt16 nCjkHeight = 0;
sal_uInt16 nCtlHeight = 0;
sal_uInt16 nDefHeight;
sal_uInt8 nDefScript = ScGlobal::GetDefaultScriptType();
if ( nDefScript == SCRIPTTYPE_ASIAN )
nDefHeight = nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
else if ( nDefScript == SCRIPTTYPE_COMPLEX )
nDefHeight = nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
else
nDefHeight = nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
// if everything below is already larger, the loop doesn't have to
// be run again
SCROW nStdEnd = nEnd;
if ( nDefHeight <= nMinHeight && nStdEnd >= nMinStart )
nStdEnd = (nMinStart>0) ? nMinStart-1 : 0;
for (SCROW nRow = nStart; nRow <= nStdEnd; ++nRow)
if (nDefHeight > rHeights[nRow-nStartRow])
rHeights[nRow-nStartRow] = nDefHeight;
if ( bStdOnly )
{
// if cells are not handled individually below,
// check for cells with different script type
sc::CellTextAttrStoreType::iterator itAttr = maCellTextAttrs.begin();
sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
sc::CellStoreType::iterator itCells = maCells.begin();
for (; it != itEnd; ++it)
{
for (SCROW nRow = it->mnRow1; nRow <= it->mnRow2; ++nRow)
{
sal_uInt8 nScript = GetRangeScriptType(itAttr, nRow, nRow, itCells);
if (nScript == nDefScript)
continue;
if ( nScript == SCRIPTTYPE_ASIAN )
{
if ( nCjkHeight == 0 )
nCjkHeight = lcl_GetAttribHeight( *pPattern, ATTR_CJK_FONT_HEIGHT );
if (nCjkHeight > rHeights[nRow-nStartRow])
rHeights[nRow-nStartRow] = nCjkHeight;
}
else if ( nScript == SCRIPTTYPE_COMPLEX )
{
if ( nCtlHeight == 0 )
nCtlHeight = lcl_GetAttribHeight( *pPattern, ATTR_CTL_FONT_HEIGHT );
if (nCtlHeight > rHeights[nRow-nStartRow])
rHeights[nRow-nStartRow] = nCtlHeight;
}
else
{
if ( nLatHeight == 0 )
nLatHeight = lcl_GetAttribHeight( *pPattern, ATTR_FONT_HEIGHT );
if (nLatHeight > rHeights[nRow-nStartRow])
rHeights[nRow-nStartRow] = nLatHeight;
}
}
}
}
}
if (!bStdOnly) // search covered cells
{
ScNeededSizeOptions aOptions;
sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
for (; it != itEnd; ++it)
{
for (SCROW nRow = it->mnRow1; nRow <= it->mnRow2; ++nRow)
{
// only calculate the cell height when it's used later (#37928#)
if (rCxt.isForceAutoSize() || !(pDocument->GetRowFlags(nRow, nTab) & CR_MANUALSIZE) )
{
aOptions.pPattern = pPattern;
const ScPatternAttr* pOldPattern = pPattern;
sal_uInt16 nHeight = (sal_uInt16)
( GetNeededSize( nRow, rCxt.getOutputDevice(), rCxt.getPPTX(), rCxt.getPPTY(),
rCxt.getZoomX(), rCxt.getZoomY(), false, aOptions,
&pPattern) / rCxt.getPPTY() );
if (nHeight > rHeights[nRow-nStartRow])
rHeights[nRow-nStartRow] = nHeight;
// Pattern changed due to calculation? => sync.
if (pPattern != pOldPattern)
{
pPattern = aIter.Resync( nRow, nStart, nEnd);
nNextEnd = 0;
}
}
}
}
}
}
if (nNextEnd > 0)
{
nStart = nEnd + 1;
nEnd = nNextEnd;
nNextEnd = 0;
}
else
pPattern = aIter.Next(nStart,nEnd);
}
}
bool ScColumn::GetNextSpellingCell(SCROW& nRow, bool bInSel, const ScMarkData& rData) const
{
bool bStop = false;
sc::CellStoreType::const_iterator it = maCells.position(nRow).first;
mdds::mtv::element_t eType = it->type;
if (!bInSel && it != maCells.end() && eType != sc::element_type_empty)
{
if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return true;
}
while (!bStop)
{
if (bInSel)
{
nRow = rData.GetNextMarked(nCol, nRow, false);
if (!ValidRow(nRow))
{
nRow = MAXROW+1;
bStop = true;
}
else
{
it = maCells.position(it, nRow).first;
eType = it->type;
if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return true;
else
nRow++;
}
}
else if (GetNextDataPos(nRow))
{
it = maCells.position(it, nRow).first;
eType = it->type;
if ( (eType == sc::element_type_string || eType == sc::element_type_edittext) &&
!(HasAttrib( nRow, nRow, HASATTR_PROTECTED) &&
pDocument->IsTabProtected(nTab)) )
return true;
else
nRow++;
}
else
{
nRow = MAXROW+1;
bStop = true;
}
}
return false;
}
namespace {
class StrEntries
{
sc::CellStoreType& mrCells;
protected:
struct StrEntry
{
SCROW mnRow;
OUString maStr;
StrEntry(SCROW nRow, const OUString& rStr) : mnRow(nRow), maStr(rStr) {}
};
std::vector<StrEntry> maStrEntries;
ScDocument* mpDoc;
StrEntries(sc::CellStoreType& rCells, ScDocument* pDoc) : mrCells(rCells), mpDoc(pDoc) {}
public:
void commitStrings()
{
svl::SharedStringPool& rPool = mpDoc->GetSharedStringPool();
sc::CellStoreType::iterator it = mrCells.begin();
std::vector<StrEntry>::iterator itStr = maStrEntries.begin(), itStrEnd = maStrEntries.end();
for (; itStr != itStrEnd; ++itStr)
it = mrCells.set(it, itStr->mnRow, rPool.intern(itStr->maStr));
}
};
class RemoveEditAttribsHandler : public StrEntries
{
boost::scoped_ptr<ScFieldEditEngine> mpEngine;
public:
RemoveEditAttribsHandler(sc::CellStoreType& rCells, ScDocument* pDoc) : StrEntries(rCells, pDoc) {}
void operator() (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(mpDoc, mpDoc->GetEditPool()));
// EE_CNTRL_ONLINESPELLING if there are errors already
mpEngine->SetControlWord(mpEngine->GetControlWord() | EE_CNTRL_ONLINESPELLING);
mpDoc->ApplyAsianEditSettings(*mpEngine);
}
mpEngine->SetText(*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(true);
bool bSpellErrors = mpEngine->HasOnlineSpellErrors();
bool bNeedObject = bSpellErrors || nParCount>1; // keep errors/paragraphs
// ScEditAttrTester is not needed anymore, arrays are gone
if (bNeedObject) // remains edit cell
{
sal_uInt32 nCtrl = mpEngine->GetControlWord();
sal_uInt32 nWantBig = bSpellErrors ? EE_CNTRL_ALLOWBIGOBJS : 0;
if ( ( nCtrl & EE_CNTRL_ALLOWBIGOBJS ) != nWantBig )
mpEngine->SetControlWord( (nCtrl & ~EE_CNTRL_ALLOWBIGOBJS) | nWantBig );
// Overwrite the existing object.
delete pObj;
pObj = mpEngine->CreateTextObject();
}
else // create String
{
// Store the string replacement for later commits.
OUString aText = ScEditUtil::GetSpaceDelimitedString(*mpEngine);
maStrEntries.push_back(StrEntry(nRow, aText));
}
}
};
class TestTabRefAbsHandler
{
SCTAB mnTab;
bool mbTestResult;
public:
TestTabRefAbsHandler(SCTAB nTab) : mnTab(nTab), mbTestResult(false) {}
void operator() (size_t /*nRow*/, const ScFormulaCell* pCell)
{
if (const_cast<ScFormulaCell*>(pCell)->TestTabRefAbs(mnTab))
mbTestResult = true;
}
bool getTestResult() const { return mbTestResult; }
};
}
void ScColumn::RemoveEditAttribs( SCROW nStartRow, SCROW nEndRow )
{
RemoveEditAttribsHandler aFunc(maCells, pDocument);
sc::ProcessEditText(maCells.begin(), maCells, nStartRow, nEndRow, aFunc);
aFunc.commitStrings();
}
bool ScColumn::TestTabRefAbs(SCTAB nTable) const
{
TestTabRefAbsHandler aFunc(nTable);
sc::ParseFormula(maCells, aFunc);
return aFunc.getTestResult();
}
bool ScColumn::IsEmptyData() const
{
return maCells.block_size() == 1 && maCells.begin()->type == sc::element_type_empty;
}
namespace {
class CellCounter
{
size_t mnCount;
public:
CellCounter() : mnCount(0) {}
void operator() (
const sc::CellStoreType::value_type& node, size_t /*nOffset*/, size_t nDataSize)
{
if (node.type == sc::element_type_empty)
return;
mnCount += nDataSize;
}
size_t getCount() const { return mnCount; }
};
}
SCSIZE ScColumn::VisibleCount( SCROW nStartRow, SCROW nEndRow ) const
{
CellCounter aFunc;
sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
return aFunc.getCount();
}
bool ScColumn::HasVisibleDataAt(SCROW nRow) const
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
sc::CellStoreType::const_iterator it = aPos.first;
if (it == maCells.end())
// Likely invalid row number.
return false;
return it->type != sc::element_type_empty;
}
bool ScColumn::IsEmptyAttr() const
{
if (pAttrArray)
return pAttrArray->IsEmpty();
else
return true;
}
bool ScColumn::IsEmpty() const
{
return (IsEmptyData() && IsEmptyAttr());
}
bool ScColumn::IsEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
{
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nStartRow);
sc::CellStoreType::const_iterator it = aPos.first;
if (it == maCells.end())
// Invalid row number.
return false;
if (it->type != sc::element_type_empty)
// Non-empty cell at the start position.
return false;
// start position of next block which is not empty.
SCROW nNextRow = nStartRow + it->size - aPos.second;
return nEndRow < nNextRow;
}
bool ScColumn::IsNotesEmptyBlock(SCROW nStartRow, SCROW nEndRow) const
{
std::pair<sc::CellNoteStoreType::const_iterator,size_t> aPos = maCellNotes.position(nStartRow);
sc::CellNoteStoreType::const_iterator it = aPos.first;
if (it == maCellNotes.end())
// Invalid row number.
return false;
if (it->type != sc::element_type_empty)
// Non-empty cell at the start position.
return false;
// 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. Skip the start row.
switch (eDir)
{
case DIR_TOP:
{
// Determine the length of empty head segment.
size_t nLength = nEndRow - nStartRow;
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;
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 MAXROW;
return MAXROW - static_cast<SCROW>(it->size);
}
SCROW ScColumn::GetLastDataPos( SCROW nLastRow ) const
{
sc::CellStoreType::const_position_type aPos = maCells.position(nLastRow);
if (aPos.first->type != sc::element_type_empty)
return nLastRow;
if (aPos.first == maCells.begin())
// This is the first block, and is empty.
return 0;
return static_cast<SCROW>(aPos.first->position - 1);
}
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())
return false;
if (it->type == sc::element_type_empty)
{
if (it == maCells.begin())
// No more previous non-empty cell.
return false;
rRow -= aPos.second + 1; // Last row position of the previous block.
return true;
}
// This block is not empty.
if (aPos.second)
{
// There are preceding cells in this block. Simply move back one cell.
--rRow;
return true;
}
// This is the first cell in an non-empty block. Move back to the previous block.
if (it == maCells.begin())
// No more preceding block.
return false;
--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.
return false;
// Skip the whole empty block segment.
rRow -= it->size;
}
return true;
}
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())
return false;
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.
return false;
// Next block exists, and is non-empty.
return true;
}
if (aPos.second < it->size - 1)
{
// There are still cells following the current position.
++rRow;
return true;
}
// 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.
return false;
if (it->type == sc::element_type_empty)
{
// Next block is empty. Move to the next block.
rRow += it->size;
++it;
if (it == maCells.end())
return false;
}
return true;
}
SCROW ScColumn::FindNextVisibleRow(SCROW nRow, bool bForward) const
{
if(bForward)
{
nRow++;
SCROW nEndRow = 0;
bool bHidden = pDocument->RowHidden(nRow, nTab, NULL, &nEndRow);
if(bHidden)
return std::min<SCROW>(MAXROW, nEndRow + 1);
else
return nRow;
}
else
{
nRow--;
SCROW nStartRow = MAXROW;
bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow, NULL);
if(bHidden)
return std::max<SCROW>(0, nStartRow - 1);
else
return nRow;
}
}
SCROW ScColumn::FindNextVisibleRowWithContent(
sc::CellStoreType::const_iterator& itPos, SCROW nRow, bool bForward) const
{
if (bForward)
{
do
{
nRow++;
SCROW nEndRow = 0;
bool bHidden = pDocument->RowHidden(nRow, nTab, NULL, &nEndRow);
if (bHidden)
{
nRow = nEndRow + 1;
if(nRow >= MAXROW)
return MAXROW;
}
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
itPos = aPos.first;
if (itPos == maCells.end())
// Invalid row.
return MAXROW;
if (itPos->type != sc::element_type_empty)
return nRow;
// Move to the last cell of the current empty block.
nRow += itPos->size - aPos.second - 1;
}
while (nRow < MAXROW);
return MAXROW;
}
do
{
nRow--;
SCROW nStartRow = MAXROW;
bool bHidden = pDocument->RowHidden(nRow, nTab, &nStartRow, NULL);
if (bHidden)
{
nRow = nStartRow - 1;
if(nRow <= 0)
return 0;
}
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(itPos, nRow);
itPos = aPos.first;
if (itPos == maCells.end())
// Invalid row.
return 0;
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()
{
// TODO: Update column's "last updated" timestamp here.
mbDirtyGroups = true;
#if DEBUG_COLUMN_STORAGE
if (maCells.size() != MAXROWCOUNT)
{
cout << "ScColumn::CellStorageModified: Size of the cell array is incorrect." << endl;
cout.flush();
abort();
}
if (maCellTextAttrs.size() != MAXROWCOUNT)
{
cout << "ScColumn::CellStorageModified: Size of the cell text attribute array is incorrect." << endl;
cout.flush();
abort();
}
if (maBroadcasters.size() != MAXROWCOUNT)
{
cout << "ScColumn::CellStorageModified: Size of the broadcaster array is incorrect." << endl;
cout.flush();
abort();
}
// Make sure that these two containers are synchronized wrt empty segments.
sc::CellStoreType::const_iterator itCell = maCells.begin();
sc::CellTextAttrStoreType::const_iterator itAttr = maCellTextAttrs.begin();
// Move to the first empty blocks.
while (itCell != maCells.end() && itCell->type != sc::element_type_empty)
++itCell;
while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty)
++itAttr;
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;
while (itCell != maCells.end() && itCell->type != sc::element_type_empty)
++itCell;
++itAttr;
while (itAttr != maCellTextAttrs.end() && itAttr->type != sc::element_type_empty)
++itAttr;
}
#endif
}
#if DEBUG_COLUMN_STORAGE
namespace {
struct FormulaGroupDumper : std::unary_function<sc::CellStoreType::value_type, void>
{
void operator() (const sc::CellStoreType::value_type& rNode) const
{
if (rNode.type != sc::element_type_formula)
return;
cout << " -- formula block" << endl;
sc::formula_block::const_iterator it = sc::formula_block::begin(*rNode.data);
sc::formula_block::const_iterator itEnd = sc::formula_block::end(*rNode.data);
for (; it != itEnd; ++it)
{
const ScFormulaCell& rCell = **it;
if (!rCell.IsShared())
{
cout << " + row " << rCell.aPos.Row() << " not shared" << endl;
continue;
}
if (rCell.GetSharedTopRow() != rCell.aPos.Row())
{
cout << " + row " << rCell.aPos.Row() << " shared with top row " << rCell.GetSharedTopRow() << " with length " << rCell.GetSharedLength() << endl;
continue;
}
SCROW nLen = rCell.GetSharedLength();
cout << " * group: start=" << rCell.aPos.Row() << ", length=" << nLen << endl;
std::advance(it, nLen-1);
}
}
};
}
void ScColumn::DumpFormulaGroups() const
{
cout << "-- formua groups" << endl;
std::for_each(maCells.begin(), maCells.end(), FormulaGroupDumper());
cout << "--" << endl;
}
#endif
void ScColumn::CopyCellTextAttrsToDocument(SCROW nRow1, SCROW nRow2, ScColumn& rDestCol) const
{
rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2); // Empty the destination range first.
sc::CellTextAttrStoreType::const_iterator itBlk = maCellTextAttrs.begin(), itBlkEnd = maCellTextAttrs.end();
// Locate the top row position.
size_t nOffsetInBlock = 0;
size_t nBlockStart = 0, nBlockEnd = 0, nRowPos = static_cast<size_t>(nRow1);
for (; itBlk != itBlkEnd; ++itBlk)
{
nBlockEnd = nBlockStart + itBlk->size;
if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
{
// Found.
nOffsetInBlock = nRowPos - nBlockStart;
break;
}
}
if (itBlk == itBlkEnd)
// Specified range not found. Bail out.
return;
nRowPos = static_cast<size_t>(nRow2); // End row position.
// Keep copying until we hit the end row position.
sc::celltextattr_block::const_iterator itData, itDataEnd;
for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
{
nBlockEnd = nBlockStart + itBlk->size;
if (!itBlk->data)
{
// Empty block.
if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
// This block contains the end row.
rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nRowPos);
else
rDestCol.maCellTextAttrs.set_empty(nBlockStart + nOffsetInBlock, nBlockEnd-1);
continue;
}
// Non-empty block.
itData = sc::celltextattr_block::begin(*itBlk->data);
itDataEnd = sc::celltextattr_block::end(*itBlk->data);
std::advance(itData, nOffsetInBlock);
if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
{
// This block contains the end row. Only copy partially.
size_t nOffset = nRowPos - nBlockStart + 1;
itDataEnd = sc::celltextattr_block::begin(*itBlk->data);
std::advance(itDataEnd, nOffset);
rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
break;
}
rDestCol.maCellTextAttrs.set(nBlockStart + nOffsetInBlock, itData, itDataEnd);
}
}
namespace {
class CopyCellNotesHandler
{
ScColumn& mrDestCol;
sc::CellNoteStoreType& mrDestNotes;
sc::CellNoteStoreType::iterator miPos;
SCTAB mnSrcTab;
SCCOL mnSrcCol;
SCTAB mnDestTab;
SCCOL mnDestCol;
SCROW mnDestOffset; /// Add this to the source row position to get the destination row.
bool mbCloneCaption;
public:
CopyCellNotesHandler( const ScColumn& rSrcCol, ScColumn& rDestCol, SCROW nDestOffset, bool bCloneCaption ) :
mrDestCol(rDestCol),
mrDestNotes(rDestCol.GetCellNoteStore()),
miPos(mrDestNotes.begin()),
mnSrcTab(rSrcCol.GetTab()),
mnSrcCol(rSrcCol.GetCol()),
mnDestTab(rDestCol.GetTab()),
mnDestCol(rDestCol.GetCol()),
mnDestOffset(nDestOffset),
mbCloneCaption(bCloneCaption) {}
void operator() ( size_t nRow, const ScPostIt* p )
{
SCROW nDestRow = nRow + mnDestOffset;
ScAddress aSrcPos(mnSrcCol, nRow, mnSrcTab);
ScAddress aDestPos(mnDestCol, nDestRow, mnDestTab);
miPos = mrDestNotes.set(miPos, nDestRow, p->Clone(aSrcPos, mrDestCol.GetDoc(), aDestPos, mbCloneCaption));
}
};
}
void ScColumn::CopyCellNotesToDocument(
SCROW nRow1, SCROW nRow2, ScColumn& rDestCol, bool bCloneCaption, SCROW nRowOffsetDest ) const
{
if (IsNotesEmptyBlock(nRow1, nRow2))
// The column has no cell notes to copy between specified rows.
return;
ScDrawLayer *pDrawLayer = rDestCol.GetDoc().GetDrawLayer();
bool bWasLocked = bool();
if (pDrawLayer)
{
// Avoid O(n^2) by temporary locking SdrModel which disables broadcasting.
// Each cell note adds undo listener, and all of them would be woken up in ScPostIt::CreateCaption.
bWasLocked = pDrawLayer->isLocked();
pDrawLayer->setLock(true);
}
CopyCellNotesHandler aFunc(*this, rDestCol, nRowOffsetDest, bCloneCaption);
sc::ParseNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
if (pDrawLayer)
pDrawLayer->setLock(bWasLocked);
}
void ScColumn::DuplicateNotes(SCROW nStartRow, size_t nDataSize, ScColumn& rDestCol, sc::ColumnBlockPosition& maDestBlockPos,
bool bCloneCaption, SCROW nRowOffsetDest ) const
{
CopyCellNotesToDocument(nStartRow, nStartRow + nDataSize -1, rDestCol, bCloneCaption, nRowOffsetDest);
maDestBlockPos.miCellNotePos = rDestCol.maCellNotes.begin();
}
SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow)
{
return maBroadcasters.get<SvtBroadcaster*>(nRow);
}
const SvtBroadcaster* ScColumn::GetBroadcaster(SCROW nRow) const
{
return maBroadcasters.get<SvtBroadcaster*>(nRow);
}
const SvtBroadcaster* ScColumn::GetBroadcaster( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
{
sc::BroadcasterStoreType::const_position_type aPos = maBroadcasters.position(rBlockPos.miBroadcasterPos, nRow);
rBlockPos.miBroadcasterPos = aPos.first;
if (aPos.first->type != sc::element_type_broadcaster)
return NULL;
return sc::broadcaster_block::at(*aPos.first->data, aPos.second);
}
void ScColumn::DeleteBroadcasters( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 )
{
rBlockPos.miBroadcasterPos =
maBroadcasters.set_empty(rBlockPos.miBroadcasterPos, nRow1, nRow2);
}
void ScColumn::PrepareBroadcastersForDestruction()
{
sc::BroadcasterStoreType::iterator itPos = maBroadcasters.begin(), itPosEnd = maBroadcasters.end();
for (; itPos != itPosEnd; ++itPos)
{
if (itPos->type == sc::element_type_broadcaster)
{
sc::broadcaster_block::iterator it = sc::broadcaster_block::begin(*itPos->data);
sc::broadcaster_block::iterator itEnd = sc::broadcaster_block::end(*itPos->data);
for (; it != itEnd; ++it)
(*it)->PrepareForDestruction();
}
}
}
bool ScColumn::HasBroadcaster() const
{
sc::BroadcasterStoreType::const_iterator it = maBroadcasters.begin(), itEnd = maBroadcasters.end();
for (; it != itEnd; ++it)
{
if (it->type == sc::element_type_broadcaster)
// Having a broadcaster block automatically means there is at least one broadcaster.
return true;
}
return false;
}
ScPostIt* ScColumn::GetCellNote(SCROW nRow)
{
return maCellNotes.get<ScPostIt*>(nRow);
}
const ScPostIt* ScColumn::GetCellNote(SCROW nRow) const
{
return maCellNotes.get<ScPostIt*>(nRow);
}
const ScPostIt* ScColumn::GetCellNote( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
{
sc::CellNoteStoreType::const_position_type aPos = maCellNotes.position(rBlockPos.miCellNotePos, nRow);
rBlockPos.miCellNotePos = aPos.first;
if (aPos.first->type != sc::element_type_cellnote)
return NULL;
return sc::cellnote_block::at(*aPos.first->data, aPos.second);
}
void ScColumn::SetCellNote(SCROW nRow, ScPostIt* pNote)
{
//pNote->UpdateCaptionPos(ScAddress(nCol, nRow, nTab)); // TODO notes usefull ? slow import with many notes
maCellNotes.set(nRow, pNote);
}
void ScColumn::DeleteCellNotes( sc::ColumnBlockPosition& rBlockPos, SCROW nRow1, SCROW nRow2 )
{
rBlockPos.miCellNotePos =
maCellNotes.set_empty(rBlockPos.miCellNotePos, nRow1, nRow2);
}
bool ScColumn::HasCellNotes() const
{
sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
for (; it != itEnd; ++it)
{
if (it->type == sc::element_type_cellnote)
// Having a cellnote block automatically means there is at least one cell note.
return true;
}
return false;
}
SCROW ScColumn::GetCellNotesMaxRow() const
{
// hypothesis : the column has cell notes (should be checked before)
SCROW maxRow = 0;
sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
for (; it != itEnd; ++it)
{
if (it->type == sc::element_type_cellnote)
maxRow = it->position + it->size -1;
}
return maxRow;
}
SCROW ScColumn::GetCellNotesMinRow() const
{
// hypothesis : the column has cell notes (should be checked before)
SCROW minRow = 0;
bool bFound = false;
sc::CellNoteStoreType::const_iterator it = maCellNotes.begin(), itEnd = maCellNotes.end();
for (; it != itEnd && !bFound; ++it)
{
if (it->type == sc::element_type_cellnote)
{
bFound = true;
minRow = it->position;
}
}
return minRow;
}
sal_uInt16 ScColumn::GetTextWidth(SCROW nRow) const
{
return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnTextWidth;
}
void ScColumn::SetTextWidth(SCROW nRow, sal_uInt16 nWidth)
{
sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
if (aPos.first->type != sc::element_type_celltextattr)
return;
// Set new value only when the slot is not empty.
sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnTextWidth = nWidth;
CellStorageModified();
}
sal_uInt8 ScColumn::GetScriptType( SCROW nRow ) const
{
if (!ValidRow(nRow) || maCellTextAttrs.is_empty(nRow))
return 0;
return maCellTextAttrs.get<sc::CellTextAttr>(nRow).mnScriptType;
}
sal_uInt8 ScColumn::GetRangeScriptType(
sc::CellTextAttrStoreType::iterator& itPos, SCROW nRow1, SCROW nRow2, const sc::CellStoreType::iterator& itrCells )
{
if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow1 > nRow2)
return 0;
SCROW nRow = nRow1;
std::pair<sc::CellTextAttrStoreType::iterator,size_t> aRet =
maCellTextAttrs.position(itPos, nRow1);
itPos = aRet.first; // Track the position of cell text attribute array.
sal_uInt8 nScriptType = 0;
bool bUpdated = false;
if (itPos->type == sc::element_type_celltextattr)
{
sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
std::advance(it, aRet.second);
for (; it != itEnd; ++it, ++nRow)
{
if (nRow > nRow2)
return nScriptType;
sc::CellTextAttr& rVal = *it;
if (UpdateScriptType(rVal, nRow, itrCells))
bUpdated = true;
nScriptType |= rVal.mnScriptType;
}
}
else
{
// Skip this whole block.
nRow += itPos->size - aRet.second;
}
while (nRow <= nRow2)
{
++itPos;
if (itPos == maCellTextAttrs.end())
return nScriptType;
if (itPos->type != sc::element_type_celltextattr)
{
// Skip this whole block.
nRow += itPos->size;
continue;
}
sc::celltextattr_block::iterator it = sc::celltextattr_block::begin(*itPos->data);
sc::celltextattr_block::iterator itEnd = sc::celltextattr_block::end(*itPos->data);
for (; it != itEnd; ++it, ++nRow)
{
if (nRow > nRow2)
return nScriptType;
sc::CellTextAttr& rVal = *it;
if (UpdateScriptType(rVal, nRow, itrCells))
bUpdated = true;
nScriptType |= rVal.mnScriptType;
}
}
if (bUpdated)
CellStorageModified();
return nScriptType;
}
void ScColumn::SetScriptType( SCROW nRow, sal_uInt8 nType )
{
if (!ValidRow(nRow))
return;
sc::CellTextAttrStoreType::position_type aPos = maCellTextAttrs.position(nRow);
if (aPos.first->type != sc::element_type_celltextattr)
// Set new value only when the slot is already set.
return;
sc::celltextattr_block::at(*aPos.first->data, aPos.second).mnScriptType = nType;
CellStorageModified();
}
size_t ScColumn::GetFormulaHash( SCROW nRow ) const
{
const ScFormulaCell* pCell = FetchFormulaCell(nRow);
return pCell ? pCell->GetHash() : 0;
}
ScFormulaVectorState ScColumn::GetFormulaVectorState( SCROW nRow ) const
{
const ScFormulaCell* pCell = FetchFormulaCell(nRow);
return pCell ? pCell->GetVectorState() : FormulaVectorUnknown;
}
formula::FormulaTokenRef ScColumn::ResolveStaticReference( SCROW nRow )
{
std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first;
if (it == maCells.end())
// Invalid row. Return a null token.
return formula::FormulaTokenRef();
switch (it->type)
{
case sc::element_type_numeric:
{
double fVal = sc::numeric_block::at(*it->data, aPos.second);
return formula::FormulaTokenRef(new formula::FormulaDoubleToken(fVal));
}
case sc::element_type_formula:
{
ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
if (p->IsValue())
return formula::FormulaTokenRef(new formula::FormulaDoubleToken(p->GetValue()));
return formula::FormulaTokenRef(new formula::FormulaStringToken(p->GetString()));
}
case sc::element_type_string:
{
const svl::SharedString& rSS = sc::string_block::at(*it->data, aPos.second);
return formula::FormulaTokenRef(new formula::FormulaStringToken(rSS.getString()));
}
case sc::element_type_edittext:
{
const EditTextObject* pText = sc::edittext_block::at(*it->data, aPos.second);
OUString aStr = ScEditUtil::GetString(*pText, pDocument);
return formula::FormulaTokenRef(new formula::FormulaStringToken(aStr));
}
case sc::element_type_empty:
default:
// Return a value of 0.0 in all the other cases.
return formula::FormulaTokenRef(new formula::FormulaDoubleToken(0.0));
}
}
namespace {
class ToMatrixHandler
{
ScMatrix& mrMat;
SCCOL mnMatCol;
SCROW mnTopRow;
ScDocument* mpDoc;
svl::SharedStringPool& mrStrPool;
public:
ToMatrixHandler(ScMatrix& rMat, SCCOL nMatCol, SCROW nTopRow, ScDocument* pDoc) :
mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow),
mpDoc(pDoc), mrStrPool(pDoc->GetSharedStringPool()) {}
void operator() (size_t nRow, double fVal)
{
mrMat.PutDouble(fVal, mnMatCol, nRow - mnTopRow);
}
void operator() (size_t nRow, const ScFormulaCell* p)
{
// Formula cell may need to re-calculate.
ScFormulaCell& rCell = const_cast<ScFormulaCell&>(*p);
if (rCell.IsValue())
mrMat.PutDouble(rCell.GetValue(), mnMatCol, nRow - mnTopRow);
else
mrMat.PutString(rCell.GetString(), mnMatCol, nRow - mnTopRow);
}
void operator() (size_t nRow, const svl::SharedString& rSS)
{
mrMat.PutString(rSS, mnMatCol, nRow - mnTopRow);
}
void operator() (size_t nRow, const EditTextObject* pStr)
{
mrMat.PutString(mrStrPool.intern(ScEditUtil::GetString(*pStr, mpDoc)), mnMatCol, nRow - mnTopRow);
}
};
}
bool ScColumn::ResolveStaticReference( ScMatrix& rMat, SCCOL nMatCol, SCROW nRow1, SCROW nRow2 )
{
if (nRow1 > nRow2)
return false;
ToMatrixHandler aFunc(rMat, nMatCol, nRow1, pDocument);
sc::ParseAllNonEmpty(maCells.begin(), maCells, nRow1, nRow2, aFunc);
return true;
}
namespace {
struct CellBucket
{
SCSIZE mnNumValStart;
SCSIZE mnStrValStart;
std::vector<double> maNumVals;
std::vector<svl::SharedString> maStrVals;
CellBucket() : mnNumValStart(0), mnStrValStart(0) {}
void flush(ScMatrix& rMat, SCSIZE nCol)
{
if (!maNumVals.empty())
{
const double* p = &maNumVals[0];
rMat.PutDouble(p, maNumVals.size(), nCol, mnNumValStart);
reset();
}
else if (!maStrVals.empty())
{
const svl::SharedString* p = &maStrVals[0];
rMat.PutString(p, maStrVals.size(), nCol, mnStrValStart);
reset();
}
}
void reset()
{
mnNumValStart = mnStrValStart = 0;
maNumVals.clear();
maStrVals.clear();
}
};
class FillMatrixHandler
{
ScMatrix& mrMat;
size_t mnMatCol;
size_t mnTopRow;
SCCOL mnCol;
SCTAB mnTab;
ScDocument* mpDoc;
svl::SharedStringPool& mrPool;
public:
FillMatrixHandler(ScMatrix& rMat, size_t nMatCol, size_t nTopRow, SCCOL nCol, SCTAB nTab, ScDocument* pDoc) :
mrMat(rMat), mnMatCol(nMatCol), mnTopRow(nTopRow), mnCol(nCol), mnTab(nTab), mpDoc(pDoc), mrPool(pDoc->GetSharedStringPool()) {}
void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
{
size_t nMatRow = node.position + nOffset - mnTopRow;
switch (node.type)
{
case sc::element_type_numeric:
{
const double* p = &sc::numeric_block::at(*node.data, nOffset);
mrMat.PutDouble(p, nDataSize, mnMatCol, nMatRow);
}
break;
case sc::element_type_string:
{
const svl::SharedString* p = &sc::string_block::at(*node.data, nOffset);
mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
}
break;
case sc::element_type_edittext:
{
std::vector<svl::SharedString> aSSs;
aSSs.reserve(nDataSize);
sc::edittext_block::const_iterator it = sc::edittext_block::begin(*node.data);
std::advance(it, nOffset);
sc::edittext_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize);
for (; it != itEnd; ++it)
{
OUString aStr = ScEditUtil::GetString(**it, mpDoc);
aSSs.push_back(mrPool.intern(aStr));
}
const svl::SharedString* p = &aSSs[0];
mrMat.PutString(p, nDataSize, mnMatCol, nMatRow);
}
break;
case sc::element_type_formula:
{
CellBucket aBucket;
sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
std::advance(it, nOffset);
sc::formula_block::const_iterator itEnd = it;
std::advance(itEnd, nDataSize);
size_t nPrevRow = 0, nThisRow = node.position + nOffset;
for (; it != itEnd; ++it, nPrevRow = nThisRow, ++nThisRow)
{
ScFormulaCell& rCell = const_cast<ScFormulaCell&>(**it);
if (rCell.IsEmpty())
{
aBucket.flush(mrMat, mnMatCol);
continue;
}
sal_uInt16 nErr;
double fVal;
if (rCell.GetErrorOrValue(nErr, fVal))
{
ScAddress aAdr(mnCol, nThisRow, mnTab);
if (nErr)
fVal = CreateDoubleError(nErr);
if (!aBucket.maNumVals.empty() && nThisRow == nPrevRow + 1)
{
// Secondary numbers.
aBucket.maNumVals.push_back(fVal);
}
else
{
// First number.
aBucket.flush(mrMat, mnMatCol);
aBucket.mnNumValStart = nThisRow - mnTopRow;
aBucket.maNumVals.push_back(fVal);
}
continue;
}
svl::SharedString aStr = rCell.GetString();
if (!aBucket.maStrVals.empty() && nThisRow == nPrevRow + 1)
{
// Secondary strings.
aBucket.maStrVals.push_back(aStr);
}
else
{
// First string.
aBucket.flush(mrMat, mnMatCol);
aBucket.mnStrValStart = nThisRow - mnTopRow;
aBucket.maStrVals.push_back(aStr);
}
}
aBucket.flush(mrMat, mnMatCol);
}
break;
default:
;
}
}
};
}
void ScColumn::FillMatrix( ScMatrix& rMat, size_t nMatCol, SCROW nRow1, SCROW nRow2 ) const
{
FillMatrixHandler aFunc(rMat, nMatCol, nRow1, nCol, nTab, pDocument);
sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
}
namespace {
template<typename _Blk>
void getBlockIterators(
const sc::CellStoreType::iterator& it, size_t& rLenRemain,
typename _Blk::iterator& rData, typename _Blk::iterator& rDataEnd )
{
rData = _Blk::begin(*it->data);
if (rLenRemain >= it->size)
{
// Block is shorter than the remaining requested length.
rDataEnd = _Blk::end(*it->data);
rLenRemain -= it->size;
}
else
{
rDataEnd = rData;
std::advance(rDataEnd, rLenRemain);
rLenRemain = 0;
}
}
bool appendToBlock(
ScDocument* pDoc, sc::FormulaGroupContext& rCxt, sc::FormulaGroupContext::ColArray& rColArray,
size_t nPos, size_t nArrayLen, const sc::CellStoreType::iterator& _it, const sc::CellStoreType::iterator& itEnd )
{
svl::SharedStringPool& rPool = pDoc->GetSharedStringPool();
size_t nLenRemain = nArrayLen - nPos;
double fNan;
rtl::math::setNan(&fNan);
for (sc::CellStoreType::iterator it = _it; it != itEnd; ++it)
{
switch (it->type)
{
case sc::element_type_string:
{
sc::string_block::iterator itData, itDataEnd;
getBlockIterators<sc::string_block>(it, nLenRemain, itData, itDataEnd);
rCxt.ensureStrArray(rColArray, nArrayLen);
for (; itData != itDataEnd; ++itData, ++nPos)
(*rColArray.mpStrArray)[nPos] = itData->getDataIgnoreCase();
}
break;
case sc::element_type_edittext:
{
sc::edittext_block::iterator itData, itDataEnd;
getBlockIterators<sc::edittext_block>(it, nLenRemain, itData, itDataEnd);
rCxt.ensureStrArray(rColArray, nArrayLen);
for (; itData != itDataEnd; ++itData, ++nPos)
{
OUString aStr = ScEditUtil::GetString(**itData, pDoc);
(*rColArray.mpStrArray)[nPos] = rPool.intern(aStr).getDataIgnoreCase();
}
}
break;
case sc::element_type_formula:
{
sc::formula_block::iterator itData, itDataEnd;
getBlockIterators<sc::formula_block>(it, nLenRemain, itData, itDataEnd);
for (; itData != itDataEnd; ++itData, ++nPos)
{
ScFormulaCell& rFC = **itData;
sc::FormulaResultValue aRes = rFC.GetResult();
if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError)
{
if (aRes.mnError == ScErrorCodes::errCircularReference)
{
// This cell needs to be recalculated on next visit.
rFC.SetErrCode(0);
rFC.SetDirtyVar();
}
return false;
}
if (aRes.meType == sc::FormulaResultValue::String)
{
rCxt.ensureStrArray(rColArray, nArrayLen);
(*rColArray.mpStrArray)[nPos] = aRes.maString.getDataIgnoreCase();
}
else
{
rCxt.ensureNumArray(rColArray, nArrayLen);
(*rColArray.mpNumArray)[nPos] = aRes.mfValue;
}
}
}
break;
case sc::element_type_empty:
{
if (nLenRemain > it->size)
{
nPos += it->size;
nLenRemain -= it->size;
}
else
{
nPos = nArrayLen;
nLenRemain = 0;
}
}
break;
case sc::element_type_numeric:
{
sc::numeric_block::iterator itData, itDataEnd;
getBlockIterators<sc::numeric_block>(it, nLenRemain, itData, itDataEnd);
rCxt.ensureNumArray(rColArray, nArrayLen);
for (; itData != itDataEnd; ++itData, ++nPos)
(*rColArray.mpNumArray)[nPos] = *itData;
}
break;
default:
return false;
}
if (!nLenRemain)
return true;
}
return false;
}
void copyFirstStringBlock(
ScDocument& rDoc, sc::FormulaGroupContext::StrArrayType& rArray, size_t nLen, const sc::CellStoreType::iterator& itBlk )
{
sc::FormulaGroupContext::StrArrayType::iterator itArray = rArray.begin();
switch (itBlk->type)
{
case sc::element_type_string:
{
sc::string_block::iterator it = sc::string_block::begin(*itBlk->data);
sc::string_block::iterator itEnd = it;
std::advance(itEnd, nLen);
for (; it != itEnd; ++it, ++itArray)
*itArray = it->getDataIgnoreCase();
}
break;
case sc::element_type_edittext:
{
sc::edittext_block::iterator it = sc::edittext_block::begin(*itBlk->data);
sc::edittext_block::iterator itEnd = it;
std::advance(itEnd, nLen);
svl::SharedStringPool& rPool = rDoc.GetSharedStringPool();
for (; it != itEnd; ++it, ++itArray)
{
EditTextObject* pText = *it;
OUString aStr = ScEditUtil::GetString(*pText, &rDoc);
*itArray = rPool.intern(aStr).getDataIgnoreCase();
}
}
break;
default:
;
}
}
sc::FormulaGroupContext::ColArray*
copyFirstFormulaBlock(
sc::FormulaGroupContext& rCxt, const sc::CellStoreType::iterator& itBlk, size_t nArrayLen,
SCTAB nTab, SCCOL nCol )
{
double fNan;
rtl::math::setNan(&fNan);
size_t nLen = std::min(itBlk->size, nArrayLen);
sc::formula_block::iterator it = sc::formula_block::begin(*itBlk->data);
sc::formula_block::iterator itEnd;
sc::FormulaGroupContext::NumArrayType* pNumArray = NULL;
sc::FormulaGroupContext::StrArrayType* pStrArray = NULL;
itEnd = it;
std::advance(itEnd, nLen);
size_t nPos = 0;
for (; it != itEnd; ++it, ++nPos)
{
ScFormulaCell& rFC = **it;
sc::FormulaResultValue aRes = rFC.GetResult();
if (aRes.meType == sc::FormulaResultValue::Invalid || aRes.mnError)
{
if (aRes.mnError == ScErrorCodes::errCircularReference)
{
// This cell needs to be recalculated on next visit.
rFC.SetErrCode(0);
rFC.SetDirtyVar();
}
return NULL;
}
if (aRes.meType == sc::FormulaResultValue::Value)
{
if (!pNumArray)
{
rCxt.maNumArrays.push_back(
new sc::FormulaGroupContext::NumArrayType(nArrayLen, fNan));
pNumArray = &rCxt.maNumArrays.back();
}
(*pNumArray)[nPos] = aRes.mfValue;
}
else
{
if (!pStrArray)
{
rCxt.maStrArrays.push_back(
new sc::FormulaGroupContext::StrArrayType(nArrayLen, NULL));
pStrArray = &rCxt.maStrArrays.back();
}
(*pStrArray)[nPos] = aRes.maString.getDataIgnoreCase();
}
}
if (!pNumArray && !pStrArray)
// At least one of these arrays should be allocated.
return NULL;
return rCxt.setCachedColArray(nTab, nCol, pNumArray, pStrArray);
}
struct NonNullStringFinder : std::unary_function<const rtl_uString*, bool>
{
bool operator() (const rtl_uString* p) const { return p != NULL; }
};
bool hasNonEmpty( const sc::FormulaGroupContext::StrArrayType& rArray, SCROW nRow1, SCROW nRow2 )
{
// The caller has to make sure the array is at least nRow2+1 long.
sc::FormulaGroupContext::StrArrayType::const_iterator it = rArray.begin();
std::advance(it, nRow1);
sc::FormulaGroupContext::StrArrayType::const_iterator itEnd = it;
std::advance(itEnd, nRow2-nRow1+1);
return std::find_if(it, itEnd, NonNullStringFinder()) != itEnd;
}
}
formula::VectorRefArray ScColumn::FetchVectorRefArray( SCROW nRow1, SCROW nRow2 )
{
if (nRow1 > nRow2)
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
// See if the requested range is already cached.
sc::FormulaGroupContext& rCxt = pDocument->GetFormulaGroupContext();
sc::FormulaGroupContext::ColArray* pColArray = rCxt.getCachedColArray(nTab, nCol, nRow2+1);
if (pColArray)
{
const double* pNum = NULL;
if (pColArray->mpNumArray)
pNum = &(*pColArray->mpNumArray)[nRow1];
rtl_uString** pStr = NULL;
if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
pStr = &(*pColArray->mpStrArray)[nRow1];
return formula::VectorRefArray(pNum, pStr);
}
double fNan;
rtl::math::setNan(&fNan);
// We need to fetch all cell values from row 0 to nRow2 for caching purposes.
sc::CellStoreType::iterator itBlk = maCells.begin();
switch (itBlk->type)
{
case sc::element_type_numeric:
{
if (static_cast<size_t>(nRow2) < itBlk->size)
{
// Requested range falls within the first block. No need to cache.
const double* p = &sc::numeric_block::at(*itBlk->data, nRow1);
return formula::VectorRefArray(p);
}
// Allocate a new array and copy the values to it.
sc::numeric_block::const_iterator it = sc::numeric_block::begin(*itBlk->data);
sc::numeric_block::const_iterator itEnd = sc::numeric_block::end(*itBlk->data);
rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(it, itEnd));
sc::FormulaGroupContext::NumArrayType& rArray = rCxt.maNumArrays.back();
rArray.resize(nRow2+1, fNan); // allocate to the requested length.
pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, NULL);
if (!pColArray)
// Failed to insert a new cached column array.
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk;
if (!appendToBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
if (pColArray->mpStrArray)
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
else
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
}
break;
case sc::element_type_string:
case sc::element_type_edittext:
{
rCxt.maStrArrays.push_back(new sc::FormulaGroupContext::StrArrayType(nRow2+1, NULL));
sc::FormulaGroupContext::StrArrayType& rArray = rCxt.maStrArrays.back();
pColArray = rCxt.setCachedColArray(nTab, nCol, NULL, &rArray);
if (!pColArray)
// Failed to insert a new cached column array.
return formula::VectorRefArray();
if (static_cast<size_t>(nRow2) < itBlk->size)
{
// Requested range falls within the first block.
copyFirstStringBlock(*pDocument, rArray, nRow2+1, itBlk);
return formula::VectorRefArray(&rArray[nRow1]);
}
copyFirstStringBlock(*pDocument, rArray, itBlk->size, itBlk);
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk;
if (!appendToBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
if (pColArray->mpNumArray)
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
else
return formula::VectorRefArray(&(*pColArray->mpStrArray)[nRow1]);
}
break;
case sc::element_type_formula:
{
if (static_cast<size_t>(nRow2) < itBlk->size)
{
// Requested length is within a single block, and the data is
// not cached.
pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
if (!pColArray)
// Failed to insert a new cached column array.
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
const double* pNum = NULL;
rtl_uString** pStr = NULL;
if (pColArray->mpNumArray)
pNum = &(*pColArray->mpNumArray)[nRow1];
if (pColArray->mpStrArray)
pStr = &(*pColArray->mpStrArray)[nRow1];
return formula::VectorRefArray(pNum, pStr);
}
pColArray = copyFirstFormulaBlock(rCxt, itBlk, nRow2+1, nTab, nCol);
if (!pColArray)
// Failed to insert a new cached column array.
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
size_t nPos = itBlk->size;
++itBlk;
if (!appendToBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
const double* pNum = NULL;
rtl_uString** pStr = NULL;
if (pColArray->mpNumArray)
pNum = &(*pColArray->mpNumArray)[nRow1];
if (pColArray->mpStrArray)
pStr = &(*pColArray->mpStrArray)[nRow1];
return formula::VectorRefArray(pNum, pStr);
}
break;
case sc::element_type_empty:
{
// Fill the whole length with NaN's.
rCxt.maNumArrays.push_back(new sc::FormulaGroupContext::NumArrayType(nRow2+1, fNan));
sc::FormulaGroupContext::NumArrayType& rArray = rCxt.maNumArrays.back();
pColArray = rCxt.setCachedColArray(nTab, nCol, &rArray, NULL);
if (!pColArray)
// Failed to insert a new cached column array.
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
if (static_cast<size_t>(nRow2) < itBlk->size)
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
// Fill the remaining array with values from the following blocks.
size_t nPos = itBlk->size;
++itBlk;
if (!appendToBlock(pDocument, rCxt, *pColArray, nPos, nRow2+1, itBlk, maCells.end()))
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
if (pColArray->mpStrArray && hasNonEmpty(*pColArray->mpStrArray, nRow1, nRow2))
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1], &(*pColArray->mpStrArray)[nRow1]);
else
return formula::VectorRefArray(&(*pColArray->mpNumArray)[nRow1]);
}
break;
default:
;
}
return formula::VectorRefArray(formula::VectorRefArray::Invalid);
}
void ScColumn::SetFormulaResults( SCROW nRow, const double* pResults, size_t nLen )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first;
if (it->type != sc::element_type_formula)
// This is not a formula block.
return;
size_t nBlockLen = it->size - aPos.second;
if (nBlockLen < nLen)
// Result array is longer than the length of formula cells. Not good.
return;
sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
std::advance(itCell, aPos.second);
const double* pResEnd = pResults + nLen;
for (; pResults != pResEnd; ++pResults, ++itCell)
{
ScFormulaCell& rCell = **itCell;
rCell.SetResultDouble(*pResults);
rCell.ResetDirty();
rCell.SetChanged(true);
}
}
void ScColumn::SetFormulaResults( SCROW nRow, const formula::FormulaTokenRef* pResults, size_t nLen )
{
sc::CellStoreType::position_type aPos = maCells.position(nRow);
sc::CellStoreType::iterator it = aPos.first;
if (it->type != sc::element_type_formula)
// This is not a formula block.
return;
size_t nBlockLen = it->size - aPos.second;
if (nBlockLen < nLen)
// Result array is longer than the length of formula cells. Not good.
return;
sc::formula_block::iterator itCell = sc::formula_block::begin(*it->data);
std::advance(itCell, aPos.second);
const formula::FormulaTokenRef* pResEnd = pResults + nLen;
for (; pResults != pResEnd; ++pResults, ++itCell)
{
ScFormulaCell& rCell = **itCell;
rCell.SetResultToken(pResults->get());
rCell.ResetDirty();
rCell.SetChanged(true);
}
}
void ScColumn::SetNumberFormat( SCROW nRow, sal_uInt32 nNumberFormat )
{
ApplyAttr(nRow, SfxUInt32Item(ATTR_VALUE_FORMAT, nNumberFormat));
}
const ScFormulaCell* ScColumn::FetchFormulaCell( SCROW nRow ) const
{
if (!ValidRow(nRow))
return NULL;
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
sc::CellStoreType::const_iterator it = aPos.first;
if (it == maCells.end())
return NULL;
if (it->type != sc::element_type_formula)
// Not a formula cell.
return NULL;
return sc::formula_block::at(*it->data, aPos.second);
}
void ScColumn::FindDataAreaPos(SCROW& rRow, bool bDown) const
{
// If the cell is empty, find the next non-empty cell position. If the
// cell is not empty, find the last non-empty cell position in the current
// contiguous cell block.
std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rRow);
sc::CellStoreType::const_iterator it = aPos.first;
if (it == maCells.end())
// Invalid row.
return;
if (it->type == sc::element_type_empty)
{
// Current cell is empty. Find the next non-empty cell.
rRow = FindNextVisibleRowWithContent(it, rRow, bDown);
return;
}
// Current cell is not empty.
SCROW nNextRow = FindNextVisibleRow(rRow, bDown);
aPos = maCells.position(it, nNextRow);
it = aPos.first;
if (it->type == sc::element_type_empty)
{
// Next visible cell is empty. Find the next non-empty cell.
rRow = FindNextVisibleRowWithContent(it, nNextRow, bDown);
return;
}
// Next visible cell is non-empty. Find the edge that's still visible.
SCROW nLastRow = nNextRow;
do
{
nNextRow = FindNextVisibleRow(nLastRow, bDown);
if (nNextRow == nLastRow)
break;
aPos = maCells.position(it, nNextRow);
it = aPos.first;
if (it->type != sc::element_type_empty)
nLastRow = nNextRow;
}
while (it->type != sc::element_type_empty);
rRow = nLastRow;
}
bool ScColumn::HasDataAt(SCROW nRow) const
{
return maCells.get_type(nRow) != sc::element_type_empty;
}
bool ScColumn::IsAllAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray && rCol.pAttrArray)
return pAttrArray->IsAllEqual( *rCol.pAttrArray, nStartRow, nEndRow );
else
return !pAttrArray && !rCol.pAttrArray;
}
bool ScColumn::IsVisibleAttrEqual( const ScColumn& rCol, SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray && rCol.pAttrArray)
return pAttrArray->IsVisibleEqual( *rCol.pAttrArray, nStartRow, nEndRow );
else
return !pAttrArray && !rCol.pAttrArray;
}
bool ScColumn::GetFirstVisibleAttr( SCROW& rFirstRow ) const
{
if (pAttrArray)
return pAttrArray->GetFirstVisibleAttr( rFirstRow );
else
return false;
}
bool ScColumn::GetLastVisibleAttr( SCROW& rLastRow, bool bFullFormattedArea ) const
{
if (pAttrArray)
{
// row of last cell is needed
SCROW nLastData = GetLastDataPos(); // always including notes, 0 if none
return pAttrArray->GetLastVisibleAttr( rLastRow, nLastData, bFullFormattedArea );
}
else
return false;
}
bool ScColumn::HasVisibleAttrIn( SCROW nStartRow, SCROW nEndRow ) const
{
if (pAttrArray)
return pAttrArray->HasVisibleAttrIn( nStartRow, nEndRow );
else
return false;
}
namespace {
class FindUsedRowsHandler
{
typedef mdds::flat_segment_tree<SCROW,bool> UsedRowsType;
UsedRowsType& mrUsed;
UsedRowsType::const_iterator miUsed;
public:
FindUsedRowsHandler(UsedRowsType& rUsed) : mrUsed(rUsed), miUsed(rUsed.begin()) {}
void operator() (const sc::CellStoreType::value_type& node, size_t nOffset, size_t nDataSize)
{
if (node.type == sc::element_type_empty)
return;
SCROW nRow1 = node.position + nOffset;
SCROW nRow2 = nRow1 + nDataSize - 1;
miUsed = mrUsed.insert(miUsed, nRow1, nRow2+1, true).first;
}
};
}
void ScColumn::FindUsed( SCROW nStartRow, SCROW nEndRow, mdds::flat_segment_tree<SCROW,bool>& rUsed ) const
{
FindUsedRowsHandler aFunc(rUsed);
sc::ParseBlock(maCells.begin(), maCells, aFunc, nStartRow, nEndRow);
}
namespace {
void startListening(
sc::BroadcasterStoreType& rStore, sc::BroadcasterStoreType::iterator& itBlockPos, size_t nElemPos,
SCROW nRow, SvtListener& rLst)
{
switch (itBlockPos->type)
{
case sc::element_type_broadcaster:
{
// Broadcaster already exists here.
SvtBroadcaster* pBC = sc::broadcaster_block::at(*itBlockPos->data, nElemPos);
rLst.StartListening(*pBC);
}
break;
case mdds::mtv::element_type_empty:
{
// No broadcaster exists at this position yet.
SvtBroadcaster* pBC = new SvtBroadcaster;
rLst.StartListening(*pBC);
itBlockPos = rStore.set(itBlockPos, nRow, pBC); // Store the block position for next iteration.
}
break;
default:
#if DEBUG_COLUMN_STORAGE
cout << "ScColumn::StartListening: wrong block type encountered in the broadcaster storage." << endl;
cout.flush();
abort();
#else
;
#endif
}
}
}
void ScColumn::StartListening( SvtListener& rLst, SCROW nRow )
{
std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(nRow);
startListening(maBroadcasters, aPos.first, aPos.second, nRow, rLst);
}
void ScColumn::EndListening( SvtListener& rLst, SCROW nRow )
{
SvtBroadcaster* pBC = GetBroadcaster(nRow);
if (!pBC)
return;
rLst.EndListening(*pBC);
if (!pBC->HasListeners())
// There is no more listeners for this cell. Remove the broadcaster.
maBroadcasters.set_empty(nRow, nRow);
}
void ScColumn::StartListening( sc::StartListeningContext& rCxt, SCROW nRow, SvtListener& rLst )
{
if (!ValidRow(nRow))
return;
sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
if (!p)
return;
sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, nRow);
it = aPos.first; // store the block position for next iteration.
startListening(maBroadcasters, it, aPos.second, nRow, rLst);
}
void ScColumn::EndListening( sc::EndListeningContext& rCxt, SCROW nRow, SvtListener& rListener )
{
sc::ColumnBlockPosition* p = rCxt.getBlockPosition(nTab, nCol);
if (!p)
return;
sc::BroadcasterStoreType::iterator& it = p->miBroadcasterPos;
std::pair<sc::BroadcasterStoreType::iterator,size_t> aPos = maBroadcasters.position(it, nRow);
it = aPos.first; // store the block position for next iteration.
if (it->type != sc::element_type_broadcaster)
return;
SvtBroadcaster* pBC = sc::broadcaster_block::at(*it->data, aPos.second);
OSL_ASSERT(pBC);
rListener.EndListening(*pBC);
if (!pBC->HasListeners())
// There is no more listeners for this cell. Add it to the purge list for later purging.
rCxt.addEmptyBroadcasterPosition(nTab, nCol, nRow);
}
namespace {
class CompileDBFormulaHandler
{
sc::CompileFormulaContext& mrCxt;
public:
CompileDBFormulaHandler( sc::CompileFormulaContext& rCxt ) :
mrCxt(rCxt) {}
void operator() (size_t, ScFormulaCell* p)
{
p->CompileDBFormula(mrCxt);
}
};
struct CompileColRowNameFormulaHandler
{
sc::CompileFormulaContext& mrCxt;
public:
CompileColRowNameFormulaHandler( sc::CompileFormulaContext& rCxt ) : mrCxt(rCxt) {}
void operator() (size_t, ScFormulaCell* p)
{
p->CompileColRowNameFormula(mrCxt);
}
};
}
void ScColumn::CompileDBFormula( sc::CompileFormulaContext& rCxt )
{
CompileDBFormulaHandler aFunc(rCxt);
sc::ProcessFormula(maCells, aFunc);
RegroupFormulaCells();
}
void ScColumn::CompileColRowNameFormula( sc::CompileFormulaContext& rCxt )
{
CompileColRowNameFormulaHandler aFunc(rCxt);
sc::ProcessFormula(maCells, aFunc);
RegroupFormulaCells();
}
namespace {
class UpdateSubTotalHandler
{
ScFunctionData& mrData;
void update(double fVal, bool bVal)
{
if (mrData.bError)
return;
switch (mrData.eFunc)
{
case SUBTOTAL_FUNC_SUM:
case SUBTOTAL_FUNC_AVE:
{
if (!bVal)
return;
++mrData.nCount;
if (!SubTotal::SafePlus(mrData.nVal, fVal))
mrData.bError = true;
}
break;
case SUBTOTAL_FUNC_CNT: // only the value
{
if (!bVal)
return;
++mrData.nCount;
}
break;
case SUBTOTAL_FUNC_CNT2: // everything
++mrData.nCount;
break;
case SUBTOTAL_FUNC_MAX:
{
if (!bVal)
return;
if (++mrData.nCount == 1 || fVal > mrData.nVal)
mrData.nVal = fVal;
}
break;
case SUBTOTAL_FUNC_MIN:
{
if (!bVal)
return;
if (++mrData.nCount == 1 || fVal < mrData.nVal)
mrData.nVal = fVal;
}
break;
default:
{
// added to avoid warnings
}
}
}
public:
UpdateSubTotalHandler(ScFunctionData& rData) : mrData(rData) {}
void operator() (size_t /*nRow*/, double fVal)
{
update(fVal, true);
}
void operator() (size_t /*nRow*/, const svl::SharedString&)
{
update(0.0, false);
}
void operator() (size_t /*nRow*/, const EditTextObject*)
{
update(0.0, false);
}
void operator() (size_t /*nRow*/, ScFormulaCell* pCell)
{
double fVal = 0.0;
bool bVal = false;
if (mrData.eFunc != SUBTOTAL_FUNC_CNT2) // it doesn't interest us
{
if (pCell->GetErrCode())
{
if (mrData.eFunc != SUBTOTAL_FUNC_CNT) // simply remove from count
mrData.bError = true;
}
else if (pCell->IsValue())
{
fVal = pCell->GetValue();
bVal = true;
}
// otherwise text
}
update(fVal, bVal);
}
};
}
// multiple selections:
void ScColumn::UpdateSelectionFunction(
const ScRangeList& rRanges, ScFunctionData& rData, ScFlatBoolRowSegments& rHiddenRows )
{
sc::SingleColumnSpanSet aSpanSet;
aSpanSet.scan(rRanges, nTab, nCol); // mark all selected rows.
// Exclude all hidden rows.
ScFlatBoolRowSegments::RangeData aRange;
SCROW nRow = 0;
while (nRow <= MAXROW)
{
if (!rHiddenRows.getRangeData(nRow, aRange))
break;
if (aRange.mbValue)
// Hidden range detected.
aSpanSet.set(nRow, aRange.mnRow2, false);
nRow = aRange.mnRow2 + 1;
}
sc::SingleColumnSpanSet::SpansType aSpans;
aSpanSet.getSpans(aSpans);
sc::SingleColumnSpanSet::SpansType::const_iterator it = aSpans.begin(), itEnd = aSpans.end();
switch (rData.eFunc)
{
case SUBTOTAL_FUNC_SELECTION_COUNT:
{
// Simply count selected rows regardless of cell contents.
for (; it != itEnd; ++it)
rData.nCount += it->mnRow2 - it->mnRow1 + 1;
}
break;
case SUBTOTAL_FUNC_CNT2:
{
// We need to parse all non-empty cells.
sc::CellStoreType::const_iterator itCellPos = maCells.begin();
UpdateSubTotalHandler aFunc(rData);
for (; it != itEnd; ++it)
{
itCellPos = sc::ParseAllNonEmpty(
itCellPos, maCells, it->mnRow1, it->mnRow2, aFunc);
}
}
break;
default:
{
// We need to parse only numeric values.
sc::CellStoreType::const_iterator itCellPos = maCells.begin();
UpdateSubTotalHandler aFunc(rData);
for (; it != itEnd; ++it)
{
itCellPos = sc::ParseFormulaNumeric(
itCellPos, maCells, it->mnRow1, it->mnRow2, aFunc);
}
}
}
}
namespace {
class WeightedCounter
{
size_t mnCount;
public:
WeightedCounter() : mnCount(0) {}
void operator() (const sc::CellStoreType::value_type& node)
{
switch (node.type)
{
case sc::element_type_numeric:
case sc::element_type_string:
mnCount += node.size;
break;
case sc::element_type_formula:
{
// Each formula cell is worth its code length plus 5.
sc::formula_block::const_iterator it = sc::formula_block::begin(*node.data);
sc::formula_block::const_iterator itEnd = sc::formula_block::end(*node.data);
for (; it != itEnd; ++it)
{
const ScFormulaCell* p = *it;
mnCount += 5 + p->GetCode()->GetCodeLen();
}
}
break;
case sc::element_type_edittext:
// each edit-text cell is worth 50.
mnCount += node.size * 50;
break;
default:
;
}
}
size_t getCount() const { return mnCount; }
};
}
sal_uInt32 ScColumn::GetWeightedCount() const
{
WeightedCounter aFunc;
std::for_each(maCells.begin(), maCells.end(), aFunc);
return aFunc.getCount();
}
namespace {
class CodeCounter
{
size_t mnCount;
public:
CodeCounter() : mnCount(0) {}
void operator() (size_t, const ScFormulaCell* p)
{
mnCount += p->GetCode()->GetCodeLen();
}
size_t getCount() const { return mnCount; }
};
}
sal_uInt32 ScColumn::GetCodeCount() const
{
CodeCounter aFunc;
sc::ParseFormula(maCells, aFunc);
return aFunc.getCount();
}
SCSIZE ScColumn::GetPatternCount() const
{
return pAttrArray ? pAttrArray->Count() : 0;
}
SCSIZE ScColumn::GetPatternCount( SCROW nRow1, SCROW nRow2 ) const
{
return pAttrArray ? pAttrArray->Count( nRow1, nRow2 ) : 0;
}
bool ScColumn::ReservePatternCount( SCSIZE nReserve )
{
return pAttrArray && pAttrArray->Reserve( nReserve );
}
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */