Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  column.cxx   Sprache: 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 <docsh.hxx>
#include <document.hxx>
#include <table.hxx>
#include <attarray.hxx>
#include <patattr.hxx>
#include <compiler.hxx>
#include <brdcst.hxx>
#include <markdata.hxx>
#include <postit.hxx>
#include <cellvalue.hxx>
#include <tokenarray.hxx>
#include <clipcontext.hxx>
#include <types.hxx>
#include <editutil.hxx>
#include <mtvcellfunc.hxx>
#include <columnspanset.hxx>
#include <scopetools.hxx>
#include <sharedformula.hxx>
#include <refupdatecontext.hxx>
#include <listenercontext.hxx>
#include <formulagroup.hxx>
#include <drwlayer.hxx>
#include <mtvelements.hxx>
#include <bcaslot.hxx>

#include <svl/numformat.hxx>
#include <poolcach.hxx>
#include <svl/zforlist.hxx>
#include <svl/sharedstringpool.hxx>
#include <editeng/fieldupdater.hxx>
#include <formula/errorcodes.hxx>
#include <o3tl/safeint.hxx>
#include <osl/diagnose.h>

#include <map>
#include <cstdio>
#include <memory>

using ::editeng::SvxBorderLine;
using namespace formula;

namespace {

bool IsAmbiguousScriptNonZero( SvtScriptType nScript )
{
    //TODO: move to a header file
    return ( nScript != SvtScriptType::LATIN &&
             nScript != SvtScriptType::ASIAN &&
             nScript != SvtScriptType::COMPLEX &&
             nScript != SvtScriptType::NONE );
}

}

ScNeededSizeOptions::ScNeededSizeOptions() :
    aPattern(), bFormula(false), bSkipMerged(true), bGetFont(true), bTotalSize(false)
{
}

ScColumn::ScColumn(ScSheetLimits const & rSheetLimits) :
    maCellTextAttrs(rSheetLimits.GetMaxRowCount()),
    maCellNotes(sc::CellStoreEvent(this)),
    maBroadcasters(rSheetLimits.GetMaxRowCount()),
    maCells(sc::CellStoreEvent(this)),
    maSparklines(rSheetLimits.GetMaxRowCount()),
    mnBlkCountFormula(0),
    mnBlkCountCellNotes(0),
    nCol( 0 ),
    nTab( 0 ),
    mbEmptyBroadcastersPending( false )
{
    maCellNotes.resize(rSheetLimits.GetMaxRowCount());
    maCells.resize(rSheetLimits.GetMaxRowCount());
}

ScColumn::~ScColumn() COVERITY_NOEXCEPT_FALSE
{
    FreeAll();
}

void ScColumn::Init(SCCOL nNewCol, SCTAB nNewTab, ScDocument& rDoc, bool bEmptyAttrArray)
{
    nCol = nNewCol;
    nTab = nNewTab;
    if ( bEmptyAttrArray )
        InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, nullptr ));
    else
        InitAttrArray(new ScAttrArray( nCol, nTab, rDoc, &rDoc.maTabs[nTab]->aDefaultColData.AttrArray()));
}

sc::MatrixEdge ScColumn::GetBlockMatrixEdges( SCROW nRow1, SCROW nRow2, sc::MatrixEdge nMask,
        bool bNoMatrixAtAll ) const
{
    using namespace sc;

    if (!GetDoc().ValidRow(nRow1) || !GetDoc().ValidRow(nRow2) || nRow1 > nRow2)
        return MatrixEdge::Nothing;

    ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);

    if (nRow1 == nRow2)
    {
        std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
        if (aPos.first->type != sc::element_type_formula)
            return MatrixEdge::Nothing;

        const ScFormulaCell* pCell = sc::formula_block::at(*aPos.first->data, aPos.second);
        if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
            return MatrixEdge::Nothing;

        return pCell->GetMatrixEdge(GetDoc(), aOrigin);
    }

    bool bOpen = false;
    MatrixEdge nEdges = MatrixEdge::Nothing;

    std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
    sc::CellStoreType::const_iterator it = aPos.first;
    size_t nOffset = aPos.second;
    SCROW nRow = nRow1;
    for (;it != maCells.end() && nRow <= nRow2; ++it, nOffset = 0)
    {
        if (it->type != sc::element_type_formula)
        {
            // Skip this block.
            nRow += it->size - nOffset;
            continue;
        }

        size_t nRowsToRead = nRow2 - nRow + 1;
        size_t nEnd = std::min(it->size, nOffset+nRowsToRead); // last row + 1
        sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
        std::advance(itCell, nOffset);
        for (size_t i = nOffset; i < nEnd; ++itCell, ++i)
        {
            // Loop inside the formula block.
            const ScFormulaCell* pCell = *itCell;
            if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
                continue;

            nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
            if (nEdges == MatrixEdge::Nothing)
                continue;

            // A 1x1 matrix array formula is OK even for no matrix at all.
            if (bNoMatrixAtAll
                    && (nEdges != (MatrixEdge::Top | MatrixEdge::Left | MatrixEdge::Bottom | MatrixEdge::Right)))
                return MatrixEdge::Inside;  // per convention Inside

            if (nEdges & MatrixEdge::Top)
                bOpen = true;       // top edge opens, keep on looking
            else if (!bOpen)
                return nEdges | MatrixEdge::Open; // there's something that wasn't opened
            else if (nEdges & MatrixEdge::Inside)
                return nEdges;      // inside
            if (((nMask & MatrixEdge::Right) && (nEdges & MatrixEdge::Left)  && !(nEdges & MatrixEdge::Right)) ||
                ((nMask & MatrixEdge::Left)  && (nEdges & MatrixEdge::Right) && !(nEdges & MatrixEdge::Left)))
                return nEdges;      // only left/right edge

            if (nEdges & MatrixEdge::Bottom)
                bOpen = false;      // bottom edge closes
        }

        nRow += nEnd - nOffset;
    }
    if (bOpen)
        nEdges |= MatrixEdge::Open; // not closed, matrix continues

    return nEdges;
}

bool ScColumn::HasSelectionMatrixFragment(const ScMarkData& rMark, const ScRangeList& rRangeList) const
{
    using namespace sc;

    if (!rMark.IsMultiMarked())
        return false;

    ScAddress aOrigin(ScAddress::INITIALIZE_INVALID);
    ScAddress aCurOrigin = aOrigin;

    bool bOpen = false;
    for (size_t i = 0, n = rRangeList.size(); i < n; ++i)
    {
        const ScRange& r = rRangeList[i];
        if (nTab < r.aStart.Tab() || r.aEnd.Tab() < nTab)
            continue;

        if (nCol < r.aStart.Col() || r.aEnd.Col() < nCol)
            continue;

        SCROW nTop = r.aStart.Row(), nBottom = r.aEnd.Row();
        SCROW nRow = nTop;
        std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
        sc::CellStoreType::const_iterator it = aPos.first;
        size_t nOffset = aPos.second;

        for (;it != maCells.end() && nRow <= nBottom; ++it, nOffset = 0)
        {
            if (it->type != sc::element_type_formula)
            {
                // Skip this block.
                nRow += it->size - nOffset;
                continue;
            }

            // This is a formula cell block.
            size_t nRowsToRead = nBottom - nRow + 1;
            size_t nEnd = std::min(it->size, nRowsToRead);
            sc::formula_block::const_iterator itCell = sc::formula_block::begin(*it->data);
            std::advance(itCell, nOffset);
            for (size_t j = nOffset; j < nEnd; ++itCell, ++j)
            {
                // Loop inside the formula block.
                const ScFormulaCell* pCell = *itCell;
                if (pCell->GetMatrixFlag() == ScMatrixMode::NONE)
                    // cell is not a part of a matrix.
                    continue;

                MatrixEdge nEdges = pCell->GetMatrixEdge(GetDoc(), aOrigin);
                if (nEdges == MatrixEdge::Nothing)
                    continue;

                bool bFound = false;

                if (nEdges & MatrixEdge::Top)
                    bOpen = true;   // top edge opens, keep on looking
                else if (!bOpen)
                    return true;    // there's something that wasn't opened
                else if (nEdges & MatrixEdge::Inside)
                    bFound = true;  // inside, all selected?

                if (((nEdges & MatrixEdge::Left) | MatrixEdge::Right) ^ ((nEdges & MatrixEdge::Right) | MatrixEdge::Left))
                    // either left or right, but not both.
                    bFound = true;  // only left/right edge, all selected?

                if (nEdges & MatrixEdge::Bottom)
                    bOpen = false;  // bottom edge closes

                if (bFound)
                {
                    // Check if the matrix is inside the selection in its entirety.
                    //
                    // TODO: It's more efficient to skip the matrix range if
                    // it's within selection, to avoid checking it again and
                    // again.

                    if (aCurOrigin != aOrigin)
                    {   // new matrix to check?
                        aCurOrigin = aOrigin;
                        const ScFormulaCell* pFCell;
                        if (pCell->GetMatrixFlag() == ScMatrixMode::Reference)
                            pFCell = GetDoc().GetFormulaCell(aOrigin);
                        else
                            pFCell = pCell;

                        SCCOL nC;
                        SCROW nR;
                        pFCell->GetMatColsRows(nC, nR);
                        ScRange aRange(aOrigin, ScAddress(aOrigin.Col()+nC-1, aOrigin.Row()+nR-1, aOrigin.Tab()));
                        if (rMark.IsAllMarked(aRange))
                            bFound = false;
                    }
                    else
                        bFound = false;     // done already
                }

                if (bFound)
                    return true;
            }

            nRow += nEnd;
        }
    }

    return bOpen;
}

bool ScColumn::HasAttribSelection( const ScMarkData& rMark, HasAttrFlags nMask ) const
{
    bool bFound = false;

    SCROW nTop;
    SCROW nBottom;

    if (rMark.IsMultiMarked())
    {
        ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
        while (aMultiIter.Next( nTop, nBottom ) && !bFound)
        {
            if (pAttrArray->HasAttrib( nTop, nBottom, nMask ))
                bFound = true;
        }
    }

    return bFound;
}

void ScColumn::MergeSelectionPattern( ScMergePatternState& rState, const ScMarkData& rMark, bool bDeep ) const
{
    SCROW nTop;
    SCROW nBottom;

    if ( rMark.IsMultiMarked() )
    {
        const ScMultiSel& rMultiSel = rMark.GetMultiSelData();
        if ( rMultiSel.HasMarks( nCol ) )
        {
            ScMultiSelIter aMultiIter( rMultiSel, nCol );
            while (aMultiIter.Next( nTop, nBottom ))
                pAttrArray->MergePatternArea( nTop, nBottom, rState, bDeep );
        }
    }
}

const ScPatternAttr* ScColumnData::GetMostUsedPattern( SCROW nStartRow, SCROW nEndRow ) const
{
    ::std::map< const ScPatternAttr*, size_t > aAttrMap;
    const ScPatternAttr* pMaxPattern = nullptr;
    size_t nMaxCount = 0;

    ScAttrIterator aAttrIter( pAttrArray.get(), nStartRow, nEndRow, &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
    const ScPatternAttr* pPattern;
    SCROW nAttrRow1 = 0, nAttrRow2 = 0;

    while( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != nullptr )
    {
        size_t& rnCount = aAttrMap[ pPattern ];
        rnCount += (nAttrRow2 - nAttrRow1 + 1);
        if( rnCount > nMaxCount )
        {
            pMaxPattern = pPattern;
            nMaxCount = rnCount;
        }
    }

    return pMaxPattern;
}

sal_uInt32 ScColumnData::GetNumberFormat( SCROW nStartRow, SCROW nEndRow ) const
{
    SCROW nPatStartRow, nPatEndRow;
    const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
    sal_uInt32 nFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
    while (nEndRow > nPatEndRow)
    {
        nStartRow = nPatEndRow + 1;
        pPattern = pAttrArray->GetPatternRange(nPatStartRow, nPatEndRow, nStartRow);
        sal_uInt32 nTmpFormat = pPattern->GetNumberFormat(GetDoc().GetFormatTable());
        if (nFormat != nTmpFormat)
            return 0;
    }
    return nFormat;
}

void ScColumnData::ApplySelectionCache(ScItemPoolCache& rCache, SCROW nStartRow, SCROW nEndRow,
                                       ScEditDataArray* pDataArray, bool* pIsChanged)
{
    pAttrArray->ApplyCacheArea(nStartRow, nEndRow, rCache, pDataArray, pIsChanged);
}

void ScColumnData::ChangeSelectionIndent(bool bIncrement, SCROW nStartRow, SCROW nEndRow)
{
    pAttrArray->ChangeIndent(nStartRow, nEndRow, bIncrement);
}

void ScColumnData::ClearSelectionItems(const sal_uInt16* pWhich, SCROW nStartRow, SCROW nEndRow)
{
    if (!pAttrArray)
        return;

    pAttrArray->ClearItems(nStartRow, nEndRow, pWhich);
}

void ScColumn::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMark, bool bBroadcast )
{
    SCROW nTop;
    SCROW nBottom;

    if ( rMark.IsMultiMarked() )
    {
        ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
        while (aMultiIter.Next( nTop, nBottom ))
            DeleteArea(nTop, nBottom, nDelFlag, bBroadcast);
    }
}

void ScColumn::ApplyPattern( SCROW nRow, const ScPatternAttr& rPatAttr )
{
    const SfxItemSet* pSet = &rPatAttr.GetItemSet();
    ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );

    const CellAttributeHolder aPattern(pAttrArray->GetPattern( nRow ));

    //  true = keep old content

    const CellAttributeHolder& rNewPattern = aCache.ApplyTo( aPattern );

    if (!CellAttributeHolder::areSame(&rNewPattern, &aPattern))
        pAttrArray->SetPattern( nRow, rNewPattern );
}

void ScColumnData::ApplyPatternArea( SCROW nStartRow, SCROW nEndRow, const ScPatternAttr& rPatAttr,
                                 ScEditDataArray* pDataArray, boolconst pIsChanged )
{
    const SfxItemSet* pSet = &rPatAttr.GetItemSet();
    ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );
    pAttrArray->ApplyCacheArea( nStartRow, nEndRow, aCache, pDataArray, pIsChanged );
}

void ScColumn::ApplyPatternIfNumberformatIncompatible( const ScRange& rRange,
        const ScPatternAttr& rPattern, SvNumFormatType nNewType )
{
    const SfxItemSet* pSet = &rPattern.GetItemSet();
    ScItemPoolCache aCache( GetDoc().getCellAttributeHelper(), *pSet );
    SvNumberFormatter* pFormatter = GetDoc().GetFormatTable();
    SCROW nEndRow = rRange.aEnd.Row();
    for ( SCROW nRow = rRange.aStart.Row(); nRow <= nEndRow; nRow++ )
    {
        SCROW nRow1, nRow2;
        const ScPatternAttr* pPattern = pAttrArray->GetPatternRange(
            nRow1, nRow2, nRow );
        sal_uInt32 nFormat = pPattern->GetNumberFormat( pFormatter );
        SvNumFormatType nOldType = pFormatter->GetType( nFormat );
        if ( nOldType == nNewType || SvNumberFormatter::IsCompatible( nOldType, nNewType ) )
            nRow = nRow2;
        else
        {
            SCROW nNewRow1 = std::max( nRow1, nRow );
            SCROW nNewRow2 = std::min( nRow2, nEndRow );
            pAttrArray->ApplyCacheArea( nNewRow1, nNewRow2, aCache );
            nRow = nNewRow2;
        }
    }
}

void ScColumn::ApplyStyle( SCROW nRow, const ScStyleSheet* rStyle )
{
    const ScPatternAttr* pPattern = pAttrArray->GetPattern(nRow);
    ScPatternAttr* pNewPattern(new ScPatternAttr(*pPattern));
    pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(rStyle));
    pAttrArray->SetPattern(nRow, CellAttributeHolder(pNewPattern, true));
}

void ScColumnData::ApplySelectionStyle(const ScStyleSheet& rStyle, SCROW nTop, SCROW nBottom)
{
    pAttrArray->ApplyStyleArea(nTop, nBottom, rStyle);
}

void ScColumn::ApplySelectionLineStyle( const ScMarkData& rMark,
                                    const SvxBorderLine* pLine, bool bColorOnly )
{
    if ( bColorOnly && !pLine )
        return;

    SCROW nTop;
    SCROW nBottom;

    if (rMark.IsMultiMarked())
    {
        ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
        while (aMultiIter.Next( nTop, nBottom ))
            pAttrArray->ApplyLineStyleArea(nTop, nBottom, pLine, bColorOnly );
    }
}

const ScStyleSheet* ScColumn::GetSelectionStyle( const ScMarkData& rMark, bool;rFound ) const
{
    rFound = false;
    if (!rMark.IsMultiMarked())
    {
        OSL_FAIL("No selection in ScColumn::GetSelectionStyle");
        return nullptr;
    }

    bool bEqual = true;

    const ScStyleSheet* pStyle = nullptr;
    const ScStyleSheet* pNewStyle;

    ScDocument& rDocument = GetDoc();
    ScMultiSelIter aMultiIter( rMark.GetMultiSelData(), nCol );
    SCROW nTop;
    SCROW nBottom;
    while (bEqual && aMultiIter.Next( nTop, nBottom ))
    {
        ScAttrIterator aAttrIter( pAttrArray.get(), nTop, nBottom, &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
        SCROW nRow;
        SCROW nDummy;
        while (bEqual)
        {
            const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
            if (!pPattern)
                break;
            pNewStyle = pPattern->GetStyleSheet();
            rFound = true;
            if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
                bEqual = false;                                             // difference
            pStyle = pNewStyle;
        }
    }

    return bEqual ? pStyle : nullptr;
}

const ScStyleSheet* ScColumn::GetAreaStyle( bool& rFound, SCROW nRow1, SCROW nRow2 ) const
{
    rFound = false;

    bool bEqual = true;

    const ScStyleSheet* pStyle = nullptr;
    const ScStyleSheet* pNewStyle;

    ScAttrIterator aAttrIter( pAttrArray.get(), nRow1, nRow2, &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
    SCROW nRow;
    SCROW nDummy;
    while (bEqual)
    {
        const ScPatternAttr* pPattern = aAttrIter.Next( nRow, nDummy );
        if (!pPattern)
            break;
        pNewStyle = pPattern->GetStyleSheet();
        rFound = true;
        if ( !pNewStyle || ( pStyle && pNewStyle != pStyle ) )
            bEqual = false;                                             // difference
        pStyle = pNewStyle;
    }

    return bEqual ? pStyle : nullptr;
}

void ScColumn::ApplyAttr( SCROW nRow, const SfxPoolItem& rAttr )
{
    //  in order to only create a new SetItem, we don't need SfxItemPoolCache.
    //TODO: Warning: ScItemPoolCache seems to create too many Refs for the new SetItem ??

    const ScPatternAttr* pOldPattern(pAttrArray->GetPattern(nRow));
    ScPatternAttr* pNewPattern(new ScPatternAttr(*pOldPattern));
    pNewPattern->GetItemSet().Put(rAttr);

    if (!ScPatternAttr::areSame( pNewPattern, pOldPattern ))
        pAttrArray->SetPattern( nRow, CellAttributeHolder(pNewPattern, true) );
    else
        delete pNewPattern;
}

ScRefCellValue ScColumn::GetCellValue( SCROW nRow ) const
{
    std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow);
    if (aPos.first == maCells.end())
        return ScRefCellValue();

    return GetCellValue(aPos.first, aPos.second);
}

ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockPosition& rBlockPos, SCROW nRow )
{
    std::pair<sc::CellStoreType::iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
    if (aPos.first == maCells.end())
        return ScRefCellValue();

    rBlockPos.miCellPos = aPos.first; // Store this for next call.
    return GetCellValue(aPos.first, aPos.second);
}

ScRefCellValue ScColumn::GetCellValue( sc::ColumnBlockConstPosition& rBlockPos, SCROW nRow ) const
{
    std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(rBlockPos.miCellPos, nRow);
    if (aPos.first == maCells.end())
        return ScRefCellValue();

    rBlockPos.miCellPos = aPos.first; // Store this for next call.
    return GetCellValue(aPos.first, aPos.second);
}

ScRefCellValue ScColumn::GetCellValue( const sc::CellStoreType::const_iterator& itPos, size_t nOffset )
{
    switch (itPos->type)
    {
        case sc::element_type_numeric:
            // Numeric cell
            return ScRefCellValue(sc::numeric_block::at(*itPos->data, nOffset));
        case sc::element_type_string:
            // String cell
            return ScRefCellValue(&sc::string_block::at(*itPos->data, nOffset));
        case sc::element_type_edittext:
            // Edit cell
            return ScRefCellValue(sc::edittext_block::at(*itPos->data, nOffset));
        case sc::element_type_formula:
            // Formula cell
            return ScRefCellValue(sc::formula_block::at(*itPos->data, nOffset));
        default:
            return ScRefCellValue(); // empty cell
    }
}

const sc::CellTextAttr* ScColumn::GetCellTextAttr( SCROW nRow ) const
{
    sc::ColumnBlockConstPosition aBlockPos;
    aBlockPos.miCellTextAttrPos = maCellTextAttrs.begin();
    return GetCellTextAttr(aBlockPos, nRow);
}

const sc::CellTextAttr* ScColumn::GetCellTextAttr( sc::ColumnBlockConstPosition& ;rBlockPos, SCROW nRow ) const
{
    sc::CellTextAttrStoreType::const_position_type aPos = maCellTextAttrs.position(rBlockPos.miCellTextAttrPos, nRow);
    if (aPos.first == maCellTextAttrs.end())
        return nullptr;

    rBlockPos.miCellTextAttrPos = aPos.first;

    if (aPos.first->type != sc::element_type_celltextattr)
        return nullptr;

    return &sc::celltextattr_block::at(*aPos.first->data, aPos.second);
}

bool ScColumn::TestInsertCol( SCROW nStartRow, SCROW nEndRow) const
{
    if (IsEmptyData() && IsEmptyAttr())
        return true;

    // Return false if we have any non-empty cells between nStartRow and nEndRow inclusive.
    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)
        return false;

    // Get the length of the remaining empty segment.
    size_t nLen = it->size - aPos.second;
    SCROW nNextNonEmptyRow = nStartRow + nLen;
    if (nNextNonEmptyRow <= nEndRow)
        return false;

    //  AttrArray only looks for merged cells

    return pAttrArray == nullptr || pAttrArray->TestInsertCol(nStartRow, nEndRow);
}

bool ScColumn::TestInsertRow( SCROW nStartRow, SCSIZE nSize ) const
{
    //  AttrArray only looks for merged cells
    {
        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 && maCells.block_size() == 1)
            // The entire cell array is empty.
            return pAttrArray->TestInsertRow(nSize);
    }

    // See if there would be any non-empty cell that gets pushed out.

    // Find the position of the last non-empty cell below nStartRow.
    size_t nLastNonEmptyRow = GetDoc().MaxRow();
    sc::CellStoreType::const_reverse_iterator it = maCells.rbegin();
    if (it->type == sc::element_type_empty)
        nLastNonEmptyRow -= it->size;

    if (nLastNonEmptyRow < o3tl::make_unsigned(nStartRow))
        // No cells would get pushed out.
        return pAttrArray->TestInsertRow(nSize);

    if (nLastNonEmptyRow + nSize > o3tl::make_unsigned(GetDoc().MaxRow()))
        // At least one cell would get pushed out. Not good.
        return false;

    return pAttrArray->TestInsertRow(nSize);
}

void ScColumn::InsertRow( SCROW nStartRow, SCSIZE nSize )
{
    pAttrArray->InsertRow( nStartRow, nSize );

    maCellNotes.insert_empty(nStartRow, nSize);
    maCellNotes.resize(GetDoc().GetMaxRowCount());

    maSparklines.insert_empty(nStartRow, nSize);
    maSparklines.resize(GetDoc().GetSheetLimits().GetMaxRowCount());

    maBroadcasters.insert_empty(nStartRow, nSize);
    maBroadcasters.resize(GetDoc().GetMaxRowCount());

    maCellTextAttrs.insert_empty(nStartRow, nSize);
    maCellTextAttrs.resize(GetDoc().GetMaxRowCount());

    maCells.insert_empty(nStartRow, nSize);
    maCells.resize(GetDoc().GetMaxRowCount());

    CellStorageModified();

    // We *probably* don't need to broadcast here since the parent call seems
    // to take care of it.
}

namespace {

class CopyToClipHandler
{
    const ScDocument& mrSrcDoc;
    const ScColumn& mrSrcCol;
    ScColumn& mrDestCol;
    sc::ColumnBlockPosition maDestPos;
    sc::ColumnBlockPosition* mpDestPos;

    void setDefaultAttrsToDest(size_t nRow, size_t nSize)
    {
        std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
            maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
    }

public:
    CopyToClipHandler(const ScDocument& rSrcDoc, const ScColumn& rSrcCol, ScColumn&&nbsp;rDestCol, sc::ColumnBlockPosition* pDestPos) :
        mrSrcDoc(rSrcDoc), mrSrcCol(rSrcCol), mrDestCol(rDestCol), mpDestPos(pDestPos)
    {
        if (mpDestPos)
            maDestPos = *mpDestPos;
        else
            mrDestCol.InitBlockPosition(maDestPos);
    }

    ~CopyToClipHandler()
    {
        if (mpDestPos)
            *mpDestPos = maDestPos;
    }

    void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
    {
        size_t nTopRow = aNode.position + nOffset;

        bool bSet = true;

        switch (aNode.type)
        {
            case sc::element_type_numeric:
            {
                sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::numeric_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);
                maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);
            }
            break;
            case sc::element_type_string:
            {
                sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::string_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);
                maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nTopRow, it, itEnd);

            }
            break;
            case sc::element_type_edittext:
            {
                sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::edittext_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                std::vector<EditTextObject*> aCloned;
                aCloned.reserve(nDataSize);
                for (; it != itEnd; ++it)
                    aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());

                maDestPos.miCellPos = mrDestCol.GetCellStore().set(
                    maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());
            }
            break;
            case sc::element_type_formula:
            {
                sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::formula_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                std::vector<ScFormulaCell*> aCloned;
                aCloned.reserve(nDataSize);
                ScAddress aDestPos(mrDestCol.GetCol(), nTopRow, mrDestCol.GetTab());
                for (; it != itEnd; ++it, aDestPos.IncRow())
                {
                    const ScFormulaCell& rOld = **it;
                    if (rOld.GetDirty() && mrSrcCol.GetDoc().GetAutoCalc())
                        const_cast<ScFormulaCell&>(rOld).Interpret();

                    aCloned.push_back(new ScFormulaCell(rOld, mrDestCol.GetDoc(), aDestPos));
                }

                // Group the cloned formula cells.
                if (!aCloned.empty())
                    sc::SharedFormulaUtil::groupFormulaCells(aCloned.begin(), aCloned.end());

                sc::CellStoreType& rDestCells = mrDestCol.GetCellStore();
                maDestPos.miCellPos = rDestCells.set(
                    maDestPos.miCellPos, nTopRow, aCloned.begin(), aCloned.end());

                // Merge adjacent formula cell groups (if applicable).
                sc::CellStoreType::position_type aPos =
                    rDestCells.position(maDestPos.miCellPos, nTopRow);
                maDestPos.miCellPos = aPos.first;
                sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
                size_t nLastRow = nTopRow + nDataSize;
                if (nLastRow < o3tl::make_unsigned(mrSrcDoc.MaxRow()))
                {
                    aPos = rDestCells.position(maDestPos.miCellPos, nLastRow+1);
                    sc::SharedFormulaUtil::joinFormulaCellAbove(aPos);
                }
            }
            break;
            default:
                bSet = false;
        }

        if (bSet)
            setDefaultAttrsToDest(nTopRow, nDataSize);

        mrSrcCol.DuplicateNotes(nTopRow, nDataSize, mrDestCol, maDestPos, false);
        mrSrcCol.DuplicateSparklines(nTopRow, nDataSize, mrDestCol, maDestPos);
    }
};

class CopyTextAttrToClipHandler
{
    sc::CellTextAttrStoreType& mrDestAttrs;
    sc::CellTextAttrStoreType::iterator miPos;

public:
    explicit CopyTextAttrToClipHandler( sc::CellTextAttrStoreType& rAttrs ) :
        mrDestAttrs(rAttrs), miPos(mrDestAttrs.begin()) {}

    void operator() ( const sc::CellTextAttrStoreType::value_type& aNode, size_t nOffset, size_t nDataSize )
    {
        if (aNode.type != sc::element_type_celltextattr)
            return;

        sc::celltextattr_block::const_iterator it = sc::celltextattr_block::begin(*aNode.data);
        std::advance(it, nOffset);
        sc::celltextattr_block::const_iterator itEnd = it;
        std::advance(itEnd, nDataSize);

        size_t nPos = aNode.position + nOffset;
        miPos = mrDestAttrs.set(miPos, nPos, it, itEnd);
    }
};


}

void ScColumn::CopyToClip(
    sc::CopyToClipContext& rCxt, SCROW nRow1, SCROW nRow2, ScColumn& rColumn ) const
{
    if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
        pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray,
                              rCxt.isKeepScenarioFlags() ? (ScMF::All & ~ScMF::Scenario) : ScMF::All );

    {
        CopyToClipHandler aFunc(GetDoc(), *this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol));
        sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    }

    if (!rCxt.isCopyChartRanges()) // No need to copy attributes for chart ranges
    {
        CopyTextAttrToClipHandler aFunc(rColumn.maCellTextAttrs);
        sc::ParseBlock(maCellTextAttrs.begin(), maCellTextAttrs, aFunc, nRow1, nRow2);
    }

    rColumn.CellStorageModified();
}

void ScColumn::CopyStaticToDocument(
    SCROW nRow1, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScColumn& rDestCol )
{
    if (nRow1 > nRow2)
        return;

    sc::ColumnBlockPosition aDestPos;
    CopyCellTextAttrsToDocument(nRow1, nRow2, rDestCol);
    CopyCellNotesToDocument(nRow1, nRow2, rDestCol);

    // First, clear the destination column for the specified row range.
    rDestCol.maCells.set_empty(nRow1, nRow2);

    aDestPos.miCellPos = rDestCol.maCells.begin();

    ScDocument& rDocument = GetDoc();
    std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nRow1);
    sc::CellStoreType::const_iterator it = aPos.first;
    size_t nOffset = aPos.second;
    size_t nDataSize = 0;
    size_t nCurRow = nRow1;

    for (; it != maCells.end() && nCurRow <= o3tl::make_unsigned(nRow2); ++it, nOffset = 0, nCurRow += nDataSize)
    {
        bool bLastBlock = false;
        nDataSize = it->size - nOffset;
        if (nCurRow + nDataSize - 1 > o3tl::make_unsigned(nRow2))
        {
            // Truncate the block to copy to clipboard.
            nDataSize = nRow2 - nCurRow + 1;
            bLastBlock = true;
        }

        switch (it->type)
        {
            case sc::element_type_numeric:
            {
                sc::numeric_block::const_iterator itData = sc::numeric_block::begin(*it->data);
                std::advance(itData, nOffset);
                sc::numeric_block::const_iterator itDataEnd = itData;
                std::advance(itDataEnd, nDataSize);
                aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
            }
            break;
            case sc::element_type_string:
            {
                sc::string_block::const_iterator itData = sc::string_block::begin(*it->data);
                std::advance(itData, nOffset);
                sc::string_block::const_iterator itDataEnd = itData;
                std::advance(itDataEnd, nDataSize);
                aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, itData, itDataEnd);
            }
            break;
            case sc::element_type_edittext:
            {
                sc::edittext_block::const_iterator itData = sc::edittext_block::begin(*it->data);
                std::advance(itData, nOffset);
                sc::edittext_block::const_iterator itDataEnd = itData;
                std::advance(itDataEnd, nDataSize);

                // Convert to simple strings.
                std::vector<svl::SharedString> aConverted;
                aConverted.reserve(nDataSize);
                for (; itData != itDataEnd; ++itData)
                {
                    const EditTextObject& rObj = **itData;
                    svl::SharedString aSS = rDocument.GetSharedStringPool().intern(ScEditUtil::GetString(rObj, rDocument));
                    aConverted.push_back(aSS);
                }
                aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nCurRow, aConverted.begin(), aConverted.end());
            }
            break;
            case sc::element_type_formula:
            {
                sc::formula_block::const_iterator itData = sc::formula_block::begin(*it->data);
                std::advance(itData, nOffset);
                sc::formula_block::const_iterator itDataEnd = itData;
                std::advance(itDataEnd, nDataSize);

                // Interpret and convert to raw values.
                for (SCROW i = 0; itData != itDataEnd; ++itData, ++i)
                {
                    SCROW nRow = nCurRow + i;

                    ScFormulaCell& rFC = **itData;
                    if (rFC.GetDirty() && rDocument.GetAutoCalc())
                        rFC.Interpret();

                    if (rFC.GetErrCode() != FormulaError::NONE)
                        // Skip cells with error.
                        continue;

                    if (rFC.IsValue())
                        aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, rFC.GetValue());
                    else
                    {
                        svl::SharedString aSS = rFC.GetString();
                        if (aSS.isValid())
                            aDestPos.miCellPos = rDestCol.maCells.set(aDestPos.miCellPos, nRow, aSS);
                    }
                }
            }
            break;
            default:
                ;
        }

        if (bLastBlock)
            break;
    }

    // Don't forget to copy the number formats over. Charts may reference them.
    for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
    {
        sal_uInt32 nNumFmt = GetNumberFormat(rDocument.GetNonThreadedContext(), nRow);
        SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
        if (itNum != rMap.end())
            nNumFmt = itNum->second;

        rDestCol.SetNumberFormat(nRow, nNumFmt);
    }

    rDestCol.CellStorageModified();
}

void ScColumn::CopyCellToDocument( SCROW nSrcRow, SCROW nDestRow, ScColumn& rDestCol )
{
    ScDocument& rDocument = GetDoc();
    std::pair<sc::CellStoreType::const_iterator,size_t> aPos = maCells.position(nSrcRow);
    sc::CellStoreType::const_iterator it = aPos.first;
    bool bSet = true;
    switch (it->type)
    {
        case sc::element_type_numeric:
            rDestCol.maCells.set(nDestRow, sc::numeric_block::at(*it->data, aPos.second));
        break;
        case sc::element_type_string:
            rDestCol.maCells.set(nDestRow, sc::string_block::at(*it->data, aPos.second));
        break;
        case sc::element_type_edittext:
        {
            EditTextObject* p = sc::edittext_block::at(*it->data, aPos.second);
            if (&rDocument == &rDestCol.GetDoc())
                rDestCol.maCells.set(nDestRow, p->Clone().release());
            else
                rDestCol.maCells.set(nDestRow, ScEditUtil::Clone(*p, rDestCol.GetDoc()).release());
        }
        break;
        case sc::element_type_formula:
        {
            ScFormulaCell* p = sc::formula_block::at(*it->data, aPos.second);
            if (p->GetDirty() && rDocument.GetAutoCalc())
                p->Interpret();

            ScAddress aDestPos = p->aPos;
            aDestPos.SetRow(nDestRow);
            ScFormulaCell* pNew = new ScFormulaCell(*p, rDestCol.GetDoc(), aDestPos);
            rDestCol.SetFormulaCell(nDestRow, pNew);
        }
        break;
        case sc::element_type_empty:
        default:
            // empty
            rDestCol.maCells.set_empty(nDestRow, nDestRow);
            bSet = false;
    }

    if (bSet)
    {
        rDestCol.maCellTextAttrs.set(nDestRow, maCellTextAttrs.get<sc::CellTextAttr>(nSrcRow));
        ScPostIt* pNote = maCellNotes.get<ScPostIt*>(nSrcRow);
        if (pNote)
        {
            pNote = pNote->Clone(ScAddress(nCol, nSrcRow, nTab),
                                 rDestCol.GetDoc(),
                                 ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab),
                                 false).release();
            rDestCol.maCellNotes.set(nDestRow, pNote);
            pNote->UpdateCaptionPos(ScAddress(rDestCol.nCol, nDestRow, rDestCol.nTab));
        }
        else
            rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
    }
    else
    {
        rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
        rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
    }

    rDestCol.CellStorageModified();
}

namespace {

bool canCopyValue(const ScDocument& rDoc, const ScAddress& rPos, InsertDeleteFlags nFlags)
{
    sal_uInt32 nNumIndex = rDoc.GetAttr(rPos, ATTR_VALUE_FORMAT)->GetValue();
    SvNumFormatType nType = rDoc.GetFormatTable()->GetType(nNumIndex);
    if ((nType == SvNumFormatType::DATE) || (nType == SvNumFormatType::TIME) || (nType == SvNumFormatType::DATETIME))
        return ((nFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE);

    return (nFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
}

class CopyAsLinkHandler
{
    const ScColumn& mrSrcCol;
    ScColumn& mrDestCol;
    sc::ColumnBlockPosition maDestPos;
    sc::ColumnBlockPosition* mpDestPos;
    InsertDeleteFlags mnCopyFlags;

    sc::StartListeningType meListenType;

    void setDefaultAttrToDest(size_t nRow)
    {
        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
            maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
    }

    void setDefaultAttrsToDest(size_t nRow, size_t nSize)
    {
        std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
            maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
    }

    ScFormulaCell* createRefCell(size_t nRow)
    {
        ScSingleRefData aRef;
        aRef.InitAddress(ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab())); // Absolute reference.
        aRef.SetFlag3D(true);

        ScTokenArray aArr(mrDestCol.GetDoc());
        aArr.AddSingleReference(aRef);
        return new ScFormulaCell(mrDestCol.GetDoc(), ScAddress(mrDestCol.GetCol(), nRow, mrDestCol.GetTab()), aArr);
    }

    void createRefBlock(const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
    {
        size_t nTopRow = aNode.position + nOffset;

        for (size_t i = 0; i < nDataSize; ++i)
        {
            SCROW nRow = nTopRow + i;
            mrDestCol.SetFormulaCell(maDestPos, nRow, createRefCell(nRow), meListenType);
        }

        setDefaultAttrsToDest(nTopRow, nDataSize);
    }

public:
    CopyAsLinkHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos, InsertDeleteFlags nCopyFlags) :
        mrSrcCol(rSrcCol),
        mrDestCol(rDestCol),
        mpDestPos(pDestPos),
        mnCopyFlags(nCopyFlags),
        meListenType(sc::SingleCellListening)
    {
        if (mpDestPos)
            maDestPos = *mpDestPos;
    }

    ~CopyAsLinkHandler()
    {
        if (mpDestPos)
        {
            // Similar to CopyByCloneHandler, don't copy a singular iterator.
            {
                sc::ColumnBlockPosition aTempBlock;
                mrDestCol.InitBlockPosition(aTempBlock);
                maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
            }

            *mpDestPos = maDestPos;
        }
    }

    void setStartListening( bool b )
    {
        meListenType = b ? sc::SingleCellListening : sc::NoListening;
    }

    void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
    {
        size_t nRow = aNode.position + nOffset;

        if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
        {
            bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
            mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
        }

        switch (aNode.type)
        {
            case sc::element_type_numeric:
            {
                if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
                    return;

                sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::numeric_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
                for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
                {
                    if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
                        continue;

                    maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, createRefCell(nRow));
                    setDefaultAttrToDest(nRow);
                }
            }
            break;
            case sc::element_type_string:
            case sc::element_type_edittext:
            {
                if (!(mnCopyFlags & InsertDeleteFlags::STRING))
                    return;

                createRefBlock(aNode, nOffset, nDataSize);
            }
            break;
            case sc::element_type_formula:
            {
                if (!(mnCopyFlags & InsertDeleteFlags::FORMULA))
                    return;

                createRefBlock(aNode, nOffset, nDataSize);
            }
            break;
            default:
                ;
        }
    }
};

class CopyByCloneHandler
{
    const ScColumn& mrSrcCol;
    ScColumn& mrDestCol;
    sc::ColumnBlockPosition maDestPos;
    sc::ColumnBlockPosition* mpDestPos;
    svl::SharedStringPool* mpSharedStringPool;
    InsertDeleteFlags mnCopyFlags;

    sc::StartListeningType meListenType;
    ScCloneFlags mnFormulaCellCloneFlags;

    void setDefaultAttrToDest(size_t nRow)
    {
        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
            maDestPos.miCellTextAttrPos, nRow, sc::CellTextAttr());
    }

    void setDefaultAttrsToDest(size_t nRow, size_t nSize)
    {
        std::vector<sc::CellTextAttr> aAttrs(nSize); // default values
        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set(
            maDestPos.miCellTextAttrPos, nRow, aAttrs.begin(), aAttrs.end());
    }

    void cloneFormulaCell(size_t nRow, ScFormulaCell& rSrcCell)
    {
        ScAddress aDestPos(mrDestCol.GetCol(), nRow, mrDestCol.GetTab());

        bool bCloneValue          = (mnCopyFlags & InsertDeleteFlags::VALUE) != InsertDeleteFlags::NONE;
        bool bCloneDateTime       = (mnCopyFlags & InsertDeleteFlags::DATETIME) != InsertDeleteFlags::NONE;
        bool bCloneString         = (mnCopyFlags & InsertDeleteFlags::STRING) != InsertDeleteFlags::NONE;
        bool bCloneSpecialBoolean = (mnCopyFlags & InsertDeleteFlags::SPECIAL_BOOLEAN) != InsertDeleteFlags::NONE;
        bool bCloneFormula        = (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE;

        bool bForceFormula = false;

        if (bCloneSpecialBoolean)
        {
            // See if the formula consists of =TRUE() or =FALSE().
            const ScTokenArray* pCode = rSrcCell.GetCode();
            if (pCode && pCode->GetLen() == 1)
            {
                const formula::FormulaToken* p = pCode->FirstToken();
                if (p->GetOpCode() == ocTrue || p->GetOpCode() == ocFalse)
                    // This is a boolean formula.
                    bForceFormula = true;
            }
        }

        if (bForceFormula || bCloneFormula)
        {
            // Clone as formula cell.
            ScFormulaCell* pCell = new ScFormulaCell(rSrcCell, mrDestCol.GetDoc(), aDestPos, mnFormulaCellCloneFlags);
            pCell->SetDirtyVar();
            mrDestCol.SetFormulaCell(maDestPos, nRow, pCell, meListenType, rSrcCell.NeedsNumberFormat());
            setDefaultAttrToDest(nRow);
            return;
        }

        if (mrDestCol.GetDoc().IsUndo())
            return;

        if (bCloneValue)
        {
            FormulaError nErr = rSrcCell.GetErrCode();
            if (nErr != FormulaError::NONE)
            {
                // error codes are cloned with values
                ScFormulaCell* pErrCell = new ScFormulaCell(mrDestCol.GetDoc(), aDestPos);
                pErrCell->SetErrCode(nErr);
                mrDestCol.SetFormulaCell(maDestPos, nRow, pErrCell, meListenType);
                setDefaultAttrToDest(nRow);
                return;
            }
        }

        if (bCloneValue || bCloneDateTime)
        {
            if (rSrcCell.IsValue())
            {
                if (canCopyValue(mrSrcCol.GetDoc(), ScAddress(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab()), mnCopyFlags))
                {
                    maDestPos.miCellPos = mrDestCol.GetCellStore().set(
                        maDestPos.miCellPos, nRow, rSrcCell.GetValue());
                    setDefaultAttrToDest(nRow);
                }

                return;
            }
        }

        if (!bCloneString)
            return;

        svl::SharedString aStr = rSrcCell.GetString();
        if (aStr.isEmpty())
            // Don't create empty string cells.
            return;

        if (rSrcCell.IsMultilineResult())
        {
            // Clone as an edit text object.
            EditEngine& rEngine = mrDestCol.GetDoc().GetEditEngine();
            rEngine.SetText(aStr.getString());
            maDestPos.miCellPos =
                mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rEngine.CreateTextObject().release());
        }
        else
        {
            maDestPos.miCellPos =
                mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aStr);
        }

        setDefaultAttrToDest(nRow);
    }

public:
    CopyByCloneHandler(const ScColumn& rSrcCol, ScColumn& rDestCol, sc::ColumnBlockPosition* pDestPos,
            InsertDeleteFlags nCopyFlags, svl::SharedStringPool* pSharedStringPool, bool bGlobalNamesToLocal) :
        mrSrcCol(rSrcCol),
        mrDestCol(rDestCol),
        mpDestPos(pDestPos),
        mpSharedStringPool(pSharedStringPool),
        mnCopyFlags(nCopyFlags),
        meListenType(sc::SingleCellListening),
        mnFormulaCellCloneFlags(bGlobalNamesToLocal ? ScCloneFlags::NamesToLocal : ScCloneFlags::Default)
    {
        if (mpDestPos)
            maDestPos = *mpDestPos;
    }

    ~CopyByCloneHandler()
    {
        if (!mpDestPos)
            return;

        // If broadcasters were setup in the same column,
        // maDestPos.miBroadcasterPos doesn't match
        // mrDestCol.maBroadcasters because it is never passed anywhere.
        // Assign a corresponding iterator before copying all over.
        // Otherwise this may result in wrongly copying a singular
        // iterator.

        {
            /* XXX Using a temporary ColumnBlockPosition just for
             * initializing from ScColumn::maBroadcasters.begin() is ugly,
             * on the other hand we don't want to expose
             * ScColumn::maBroadcasters to the outer world and have a
             * getter. */

            sc::ColumnBlockPosition aTempBlock;
            mrDestCol.InitBlockPosition(aTempBlock);
            maDestPos.miBroadcasterPos = aTempBlock.miBroadcasterPos;
        }

        *mpDestPos = maDestPos;
    }

    void setStartListening( bool b )
    {
        meListenType = b ? sc::SingleCellListening : sc::NoListening;
    }

    void operator() (const sc::CellStoreType::value_type& aNode, size_t nOffset, size_t nDataSize)
    {
        size_t nRow = aNode.position + nOffset;

        if (mnCopyFlags & (InsertDeleteFlags::NOTE|InsertDeleteFlags::ADDNOTES))
        {
            bool bCloneCaption = (mnCopyFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
            mrSrcCol.DuplicateNotes(nRow, nDataSize, mrDestCol, maDestPos, bCloneCaption);
        }

        switch (aNode.type)
        {
            case sc::element_type_numeric:
            {
                if ((mnCopyFlags & (InsertDeleteFlags::DATETIME|InsertDeleteFlags::VALUE)) == InsertDeleteFlags::NONE)
                    return;

                sc::numeric_block::const_iterator it = sc::numeric_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::numeric_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                ScAddress aSrcPos(mrSrcCol.GetCol(), nRow, mrSrcCol.GetTab());
                for (; it != itEnd; ++it, aSrcPos.IncRow(), ++nRow)
                {
                    if (!canCopyValue(mrSrcCol.GetDoc(), aSrcPos, mnCopyFlags))
                        continue;

                    maDestPos.miCellPos = mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, *it);
                    setDefaultAttrToDest(nRow);
                }
            }
            break;
            case sc::element_type_string:
            {
                if (!(mnCopyFlags & InsertDeleteFlags::STRING))
                    return;

                sc::string_block::const_iterator it = sc::string_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::string_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                for (; it != itEnd; ++it, ++nRow)
                {
                    const svl::SharedString& rStr = *it;
                    if (rStr.isEmpty())
                    {
                        // String cell with empty value is used to special-case cell value removal.
                        maDestPos.miCellPos = mrDestCol.GetCellStore().set_empty(
                            maDestPos.miCellPos, nRow, nRow);
                        maDestPos.miCellTextAttrPos = mrDestCol.GetCellAttrStore().set_empty(
                            maDestPos.miCellTextAttrPos, nRow, nRow);
                    }
                    else
                    {
                        if (mpSharedStringPool)
                        {
                            // Re-intern the string if source is a different document.
                            svl::SharedString aInterned = mpSharedStringPool->intern( rStr.getString());
                            maDestPos.miCellPos =
                                mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, aInterned);
                        }
                        else
                        {
                            maDestPos.miCellPos =
                                mrDestCol.GetCellStore().set(maDestPos.miCellPos, nRow, rStr);
                        }
                        setDefaultAttrToDest(nRow);
                    }
                }
            }
            break;
            case sc::element_type_edittext:
            {
                if (!(mnCopyFlags & InsertDeleteFlags::STRING))
                    return;

                sc::edittext_block::const_iterator it = sc::edittext_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::edittext_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                std::vector<EditTextObject*> aCloned;
                aCloned.reserve(nDataSize);
                for (; it != itEnd; ++it)
                    aCloned.push_back(ScEditUtil::Clone(**it, mrDestCol.GetDoc()).release());

                maDestPos.miCellPos = mrDestCol.GetCellStore().set(
                    maDestPos.miCellPos, nRow, aCloned.begin(), aCloned.end());

                setDefaultAttrsToDest(nRow, nDataSize);
            }
            break;
            case sc::element_type_formula:
            {
                sc::formula_block::const_iterator it = sc::formula_block::begin(*aNode.data);
                std::advance(it, nOffset);
                sc::formula_block::const_iterator itEnd = it;
                std::advance(itEnd, nDataSize);

                sc::DelayStartListeningFormulaCells startDelay(mrDestCol); // disabled
                if(nDataSize > 1024 && (mnCopyFlags & InsertDeleteFlags::FORMULA) != InsertDeleteFlags::NONE)
                {
                    // If the column to be replaced contains a long formula group (tdf#102364), there can
                    // be so many listeners in a single vector that the quadratic cost of repeatedly removing
                    // the first element becomes very high. Optimize this by removing them in one go.
                    sc::EndListeningContext context(mrDestCol.GetDoc());
                    mrDestCol.EndListeningFormulaCells( context, nRow, nRow + nDataSize - 1, nullptr, nullptr );
                    // There can be a similar problem with starting to listen to cells repeatedly (tdf#133302).
                    // Delay it.
                    startDelay.set();
                }

                for (; it != itEnd; ++it, ++nRow)
                    cloneFormulaCell(nRow, **it);
            }
            break;
            default:
                ;
        }
    }
};

}

void ScColumn::CopyToColumn(
    sc::CopyToDocContext& rCxt,
    SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked, ScColumn& rColumn,
    const ScMarkData* pMarkData, bool bAsLink, bool bGlobalNamesToLocal) const
{
    if (bMarked)
    {
        SCROW nStart, nEnd;
        if (pMarkData && pMarkData->IsMultiMarked())
        {
            ScMultiSelIter aIter( pMarkData->GetMultiSelData(), nCol );

            while ( aIter.Next( nStart, nEnd ) && nStart <= nRow2 )
            {
                if ( nEnd >= nRow1 )
                    CopyToColumn(rCxt, std::max(nRow1,nStart), std::min(nRow2,nEnd),
                                    nFlags, false, rColumn, pMarkData, bAsLink );
            }
        }
        else
        {
            OSL_FAIL("CopyToColumn: bMarked, but no mark");
        }
        return;
    }

    if ( (nFlags & InsertDeleteFlags::ATTRIB) != InsertDeleteFlags::NONE )
    {
        if ( (nFlags & InsertDeleteFlags::STYLES) != InsertDeleteFlags::STYLES )
        {   // keep the StyleSheets in the target document
            // e.g. DIF and RTF Clipboard-Import
            for ( SCROW nRow = nRow1; nRow <= nRow2; nRow++ )
            {
                const ScStyleSheet* pStyle(rColumn.pAttrArray->GetPattern( nRow )->GetStyleSheet());
                ScPatternAttr* pNewPattern(new ScPatternAttr(*pAttrArray->GetPattern(nRow)));
                pNewPattern->SetStyleSheet(const_cast<ScStyleSheet*>(pStyle));
                rColumn.pAttrArray->SetPattern(nRow, CellAttributeHolder(pNewPattern, true));
            }
        }
        else
            pAttrArray->CopyArea( nRow1, nRow2, 0, *rColumn.pAttrArray);
    }

    if ((nFlags & InsertDeleteFlags::CONTENTS) == InsertDeleteFlags::NONE)
        return;

    if (bAsLink)
    {
        CopyAsLinkHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags);
        aFunc.setStartListening(rCxt.isStartListening());
        sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    }
    else
    {
        // Compare the ScDocumentPool* to determine if we are copying
        // within the same document. If not, re-intern shared strings.
        svl::SharedStringPool* pSharedStringPool =
            (GetDoc().GetPool() != rColumn.GetDoc().GetPool()) ?
            &rColumn.GetDoc().GetSharedStringPool() : nullptr;
        CopyByCloneHandler aFunc(*this, rColumn, rCxt.getBlockPosition(rColumn.nTab, rColumn.nCol), nFlags,
                pSharedStringPool, bGlobalNamesToLocal);
        aFunc.setStartListening(rCxt.isStartListening());
        sc::ParseBlock(maCells.begin(), maCells, aFunc, nRow1, nRow2);
    }

    rColumn.CellStorageModified();
}

void ScColumn::UndoToColumn(
    sc::CopyToDocContext& rCxt, SCROW nRow1, SCROW nRow2, InsertDeleteFlags nFlags, bool bMarked,
    ScColumn& rColumn ) const
{
    if (nRow1 > 0)
        CopyToColumn(rCxt, 0, nRow1-1, InsertDeleteFlags::FORMULA, false, rColumn);

    CopyToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rColumn);      //TODO: bMarked ????

    if (nRow2 < GetDoc().MaxRow())
        CopyToColumn(rCxt, nRow2+1, GetDoc().MaxRow(), InsertDeleteFlags::FORMULA, false, rColumn);
}

void ScColumn::CopyUpdated( const ScColumn* pPosCol, ScColumn& rDestCol ) const
{
    // Copy cells from this column to the destination column only for those
    // rows that are present in the position column (pPosCol).

    // First, mark all the non-empty cell ranges from the position column.
    sc::SingleColumnSpanSet aRangeSet(GetDoc().GetSheetLimits());
    if(pPosCol)
        aRangeSet.scan(*pPosCol);

    // Now, copy cells from this column to the destination column for those
    // marked row ranges.
    sc::SingleColumnSpanSet::SpansType aRanges;
    aRangeSet.getSpans(aRanges);

    CopyToClipHandler aFunc(GetDoc(), *this, rDestCol, nullptr);
    sc::CellStoreType::const_iterator itPos = maCells.begin();
    for (const auto& rRange : aRanges)
        itPos = sc::ParseBlock(itPos, maCells, aFunc, rRange.mnRow1, rRange.mnRow2);

    rDestCol.CellStorageModified();
}

void ScColumn::CopyScenarioFrom( const ScColumn& rSrcCol )
{
    //  This is the scenario table, the data is copied into it
    ScDocument& rDocument = GetDoc();
    ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
    SCROW nStart = -1, nEnd = -1;
    const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
    while (pPattern)
    {
        if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
        {
            DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
            sc::CopyToDocContext aCxt(rDocument);
            rSrcCol.
                CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, *this);

            //  UpdateUsed not needed, already done in TestCopyScenario (obsolete comment ?)

            sc::RefUpdateContext aRefCxt(rDocument);
            aRefCxt.meMode = URM_COPY;
            aRefCxt.maRange = ScRange(nCol, nStart, nTab, nCol, nEnd, nTab);
            aRefCxt.mnTabDelta = nTab - rSrcCol.nTab;
            UpdateReferenceOnCopy(aRefCxt);
            UpdateCompile();
        }
        pPattern = aAttrIter.Next( nStart, nEnd );
    }
}

void ScColumn::CopyScenarioTo( ScColumn& rDestCol ) const
{
    //  This is the scenario table, the data is copied to the other
    ScDocument& rDocument = GetDoc();
    ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &rDocument.getCellAttributeHelper().getDefaultCellAttribute() );
    SCROW nStart = -1, nEnd = -1;
    const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
    while (pPattern)
    {
        if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
        {
            rDestCol.DeleteArea( nStart, nEnd, InsertDeleteFlags::CONTENTS );
            sc::CopyToDocContext aCxt(rDestCol.GetDoc());
            CopyToColumn(aCxt, nStart, nEnd, InsertDeleteFlags::CONTENTS, false, rDestCol);

            sc::RefUpdateContext aRefCxt(rDocument);
            aRefCxt.meMode = URM_COPY;
            aRefCxt.maRange = ScRange(rDestCol.nCol, nStart, rDestCol.nTab, rDestCol.nCol, nEnd, rDestCol.nTab);
            aRefCxt.mnTabDelta = rDestCol.nTab - nTab;
            rDestCol.UpdateReferenceOnCopy(aRefCxt);
            rDestCol.UpdateCompile();
        }
        pPattern = aAttrIter.Next( nStart, nEnd );
    }
}

bool ScColumn::TestCopyScenarioTo( const ScColumn& rDestCol ) const
{
    bool bOk = true;
    ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
    SCROW nStart = 0, nEnd = 0;
    const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
    while (pPattern && bOk)
    {
        if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
            if ( rDestCol.pAttrArray->HasAttrib( nStart, nEnd, HasAttrFlags::Protected ) )
                bOk = false;

        pPattern = aAttrIter.Next( nStart, nEnd );
    }
    return bOk;
}

void ScColumn::MarkScenarioIn( ScMarkData& rDestMark ) const
{
    ScRange aRange( nCol, 0, nTab );

    ScAttrIterator aAttrIter( pAttrArray.get(), 0, GetDoc().MaxRow(), &GetDoc().getCellAttributeHelper().getDefaultCellAttribute() );
    SCROW nStart = -1, nEnd = -1;
    const ScPatternAttr* pPattern = aAttrIter.Next( nStart, nEnd );
    while (pPattern)
    {
        if ( pPattern->GetItem( ATTR_MERGE_FLAG ).IsScenario() )
        {
            aRange.aStart.SetRow( nStart );
            aRange.aEnd.SetRow( nEnd );
            rDestMark.SetMultiMarkArea( aRange );
        }

        pPattern = aAttrIter.Next( nStart, nEnd );
    }
}

namespace {

void resetColumnPosition(sc::CellStoreType& rCells, SCCOL nCol)
{
    for (auto& rCellItem : rCells)
    {
        if (rCellItem.type != sc::element_type_formula)
            continue;

        sc::formula_block::iterator itCell = sc::formula_block::begin(*rCellItem.data);
        sc::formula_block::iterator itCellEnd = sc::formula_block::end(*rCellItem.data);
        for (; itCell != itCellEnd; ++itCell)
        {
            ScFormulaCell& rCell = **itCell;
            rCell.aPos.SetCol(nCol);
        }
    }
}

class NoteCaptionUpdater
{
    const ScDocument& m_rDocument;
    const ScAddress m_aAddress; // 'incomplete' address consisting of tab, column
    bool m_bUpdateCaptionPos;  // false if we want to skip updating the caption pos, only useful in kit mode
    bool m_bAddressChanged;  // false if the cell anchor address is unchanged
public:
    NoteCaptionUpdater(const ScDocument& rDocument, const ScAddress& rPos, bool bUpdateCaptionPos, bool bAddressChanged)
        : m_rDocument(rDocument)
        , m_aAddress(rPos)
        , m_bUpdateCaptionPos(bUpdateCaptionPos)
        , m_bAddressChanged(bAddressChanged)
    {
    }

    void operator() ( size_t nRow, ScPostIt* p )
    {
        // Create a 'complete' address object
        ScAddress aAddr(m_aAddress);
        aAddr.SetRow(nRow);

        if (m_bUpdateCaptionPos)
            p->UpdateCaptionPos(aAddr);

        // Notify our LOK clients
        if (m_bAddressChanged)
            ScDocShell::LOKCommentNotify(LOKCommentNotificationType::Modify, m_rDocument, aAddr, p);
    }
};

}

void ScColumn::UpdateNoteCaptions( SCROW nRow1, SCROW nRow2, bool bAddressChanged )
{
    ScAddress aAddr(nCol, 0, nTab);
    NoteCaptionUpdater aFunc(GetDoc(), aAddr, true, bAddressChanged);
    sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
}

void ScColumn::CommentNotifyAddressChange( SCROW nRow1, SCROW nRow2 )
{
    ScAddress aAddr(nCol, 0, nTab);
    NoteCaptionUpdater aFunc(GetDoc(), aAddr, falsetrue);
    sc::ProcessNote(maCellNotes.begin(), maCellNotes, nRow1, nRow2, aFunc);
}

void ScColumn::UpdateDrawObjects(std::vector<std::vector<SdrObject*>>& pObjects, SCROW nRowStart, SCROW nRowEnd)
{
    assert(static_cast<int>(pObjects.size()) >= nRowEnd - nRowStart + 1);

    int nObj = 0;
    for (SCROW nCurrentRow = nRowStart; nCurrentRow <= nRowEnd; nCurrentRow++, nObj++)
    {
--> --------------------

--> maximum size reached

--> --------------------

Messung V0.5
C=94 H=88 G=90

¤ Dauer der Verarbeitung: 0.20 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

Beweissystem der NASA

Beweissystem Isabelle

NIST Cobol Testsuite

Cephes Mathematical Library

Wiener Entwicklungsmethode

Haftungshinweis

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.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge