Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/LibreOffice/sc/source/core/data/   (Office von Apache Version 25.8.3.2©)  Datei vom 5.10.2025 mit Größe 151 kB image not shown  

Quelle  table2.cxx   Sprache: C

 
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */


#include <algorithm>
#include <memory>
#include <table.hxx>
#include <patattr.hxx>
#include <docpool.hxx>
#include <formulacell.hxx>
#include <document.hxx>
#include <drwlayer.hxx>
#include <olinetab.hxx>
#include <stlpool.hxx>
#include <attarray.hxx>
#include <markdata.hxx>
#include <dociter.hxx>
#include <conditio.hxx>
#include <chartlis.hxx>
#include <fillinfo.hxx>
#include <bcaslot.hxx>
#include <postit.hxx>
#include <sheetevents.hxx>
#include <segmenttree.hxx>
#include <dbdata.hxx>
#include <tokenarray.hxx>
#include <clipcontext.hxx>
#include <types.hxx>
#include <editutil.hxx>
#include <mtvcellfunc.hxx>
#include <refupdatecontext.hxx>
#include <scopetools.hxx>
#include <tabprotection.hxx>
#include <columnspanset.hxx>
#include <rowheightcontext.hxx>
#include <listenercontext.hxx>
#include <compressedarray.hxx>
#include <refdata.hxx>
#include <docsh.hxx>
#include <dpobject.hxx>

#include <scitems.hxx>
#include <editeng/boxitem.hxx>
#include <editeng/editobj.hxx>
#include <o3tl/safeint.hxx>
#include <o3tl/unit_conversion.hxx>
#include <osl/diagnose.h>
#include <sal/log.hxx>
#include <poolcach.hxx>
#include <unotools/charclass.hxx>
#include <math.h>

namespace {

class ColumnRegroupFormulaCells
{
    ScColContainer& mrCols;
    std::vector<ScAddress>* mpGroupPos;

public:
    ColumnRegroupFormulaCells( ScColContainer& rCols, std::vector<ScAddress>* pGroupPos ) :
        mrCols(rCols), mpGroupPos(pGroupPos) {}

    void operator() (SCCOL nCol)
    {
        mrCols[nCol].RegroupFormulaCells(mpGroupPos);
    }
};

}

sal_uInt16 ScTable::GetTextWidth(SCCOL nCol, SCROW nRow) const
{
    return aCol[nCol].GetTextWidth(nRow);
}

bool ScTable::SetOutlineTable( const ScOutlineTable* pNewOutline )
{
    sal_uInt16 nOldSizeX = 0;
    sal_uInt16 nOldSizeY = 0;
    sal_uInt16 nNewSizeX = 0;
    sal_uInt16 nNewSizeY = 0;

    if (pOutlineTable)
    {
        nOldSizeX = pOutlineTable->GetColArray().GetDepth();
        nOldSizeY = pOutlineTable->GetRowArray().GetDepth();
        pOutlineTable.reset();
    }

    if (pNewOutline)
    {
        pOutlineTable.reset(new ScOutlineTable( *pNewOutline ));
        nNewSizeX = pOutlineTable->GetColArray().GetDepth();
        nNewSizeY = pOutlineTable->GetRowArray().GetDepth();
    }

    return ( nNewSizeX != nOldSizeX || nNewSizeY != nOldSizeY );        // changed size?
}

void ScTable::StartOutlineTable()
{
    if (!pOutlineTable)
        pOutlineTable.reset(new ScOutlineTable);
}

void ScTable::SetSheetEvents( std::unique_ptr<ScSheetEvents> pNew )
{
    pSheetEvents = std::move(pNew);

    SetCalcNotification( false );       // discard notifications before the events were set

    SetStreamValid(false);
}

void ScTable::SetCalcNotification( bool bSet )
{
    bCalcNotification = bSet;
}

bool ScTable::TestInsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize ) const
{
    if ( nStartCol==0 && nEndCol==rDocument.MaxCol() && pOutlineTable )
        if (!pOutlineTable->TestInsertRow(nSize))
            return false;

    SCCOL maxCol = ClampToAllocatedColumns(nEndCol);
    for (SCCOL i=nStartCol; i<=maxCol; i++)
        if (!aCol[i].TestInsertRow(nStartRow, nSize))
            return false;

    if( maxCol != nEndCol )
        if (!aDefaultColData.TestInsertRow(nSize))
            return false;

    return true;
}

void ScTable::InsertRow( SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize )
{
    if (nStartCol==0 && nEndCol==rDocument.MaxCol())
    {
        if (mpRowHeights && pRowFlags)
        {
            mpRowHeights->insertSegment(nStartRow, nSize);
            CRFlags nNewFlags = pRowFlags->Insert( nStartRow, nSize);
            // only copy manual size flag, clear all others
            if (nNewFlags != CRFlags::NONE && (nNewFlags != CRFlags::ManualSize))
                pRowFlags->SetValue( nStartRow, nStartRow + nSize - 1,
                        nNewFlags & CRFlags::ManualSize);
        }

        if (pOutlineTable)
            pOutlineTable->InsertRow( nStartRow, nSize );

        mpFilteredRows->insertSegment(nStartRow, nSize);
        mpHiddenRows->insertSegment(nStartRow, nSize);

        if (!maRowManualBreaks.empty())
        {
            // Copy all breaks up to nStartRow (non-inclusive).
            ::std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
            ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);

            // Copy all breaks from nStartRow (inclusive) to the last element,
            // but add nSize to each value.
            ::std::set<SCROW>::iterator itr2 = maRowManualBreaks.end();
            for (; itr1 != itr2; ++itr1)
                aNewBreaks.insert(static_cast<SCROW>(*itr1 + nSize));

            maRowManualBreaks.swap(aNewBreaks);
        }
    }

    for (SCCOL j : GetAllocatedColumnsRange(nStartCol, nEndCol))
        aCol[j].InsertRow( nStartRow, nSize );
    aDefaultColData.InsertRow( nStartRow, nSize );

    mpCondFormatList->InsertRow(nTab, nStartCol, nEndCol, nStartRow, nSize);

    InvalidatePageBreaks();

    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

void ScTable::DeleteRow(
    const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCCOL nEndCol, SCROW nStartRow, SCSIZE nSize,
    bool* pUndoOutline, std::vector<ScAddress>* pGroupPos )
{
    if (nStartCol==0 && nEndCol==rDocument.MaxCol())
    {
        if (pRowFlags)
            pRowFlags->Remove( nStartRow, nSize);

        if (mpRowHeights)
            mpRowHeights->removeSegment(nStartRow, nStartRow+nSize);

        if (pOutlineTable)
            if (pOutlineTable->DeleteRow( nStartRow, nSize ))
                if (pUndoOutline)
                    *pUndoOutline = true;

        mpFilteredRows->removeSegment(nStartRow, nStartRow+nSize);
        mpHiddenRows->removeSegment(nStartRow, nStartRow+nSize);

        if (!maRowManualBreaks.empty())
        {
            // Erase all manual breaks between nStartRow and nStartRow + nSize - 1 (inclusive).
            std::set<SCROW>::iterator itr1 = maRowManualBreaks.lower_bound(nStartRow);
            std::set<SCROW>::iterator itr2 = maRowManualBreaks.upper_bound(static_cast<SCROW>(nStartRow + nSize - 1));
            maRowManualBreaks.erase(itr1, itr2);

            // Copy all breaks from the 1st element up to nStartRow to the new container.
            itr1 = maRowManualBreaks.lower_bound(nStartRow);
            ::std::set<SCROW> aNewBreaks(maRowManualBreaks.begin(), itr1);

            // Copy all breaks from nStartRow to the last element, but subtract each value by nSize.
            itr2 = maRowManualBreaks.end();
            for (; itr1 != itr2; ++itr1)
                aNewBreaks.insert(static_cast<SCROW>(*itr1 - nSize));

            maRowManualBreaks.swap(aNewBreaks);
        }
    }

    {   // scope for bulk broadcast
        ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
        for (SCCOL j=nStartCol; j<=ClampToAllocatedColumns(nEndCol); j++)
            aCol[j].DeleteRow(nStartRow, nSize, pGroupPos);
    }

    std::vector<SCCOL> aRegroupCols;
    rRegroupCols.getColumns(nTab, aRegroupCols);
    std::for_each(
        aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, pGroupPos));

    InvalidatePageBreaks();

    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

bool ScTable::TestInsertCol( SCROW nStartRow, SCROW nEndRow, SCSIZE nSize ) const
{
    if ( nSize > o3tl::make_unsigned(rDocument.MaxCol()) )
        return false;

    if ( nStartRow==0 && nEndRow==rDocument.MaxRow() && pOutlineTable
        && ! pOutlineTable->TestInsertCol(nSize) )
            return false;

    auto range = GetAllocatedColumnsRange( rDocument.MaxCol() - static_cast<SCCOL>(nSize) + 1, rDocument.MaxCol() );
    for (auto it = range.rbegin(); it != range.rend(); ++it )
        if (! aCol[*it].TestInsertCol(nStartRow, nEndRow))
            return false;

    return true;
}

void ScTable::InsertCol(
    const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize )
{
    if (nStartRow==0 && nEndRow==rDocument.MaxRow())
    {
        if (mpColWidth && mpColFlags)
        {
            mpColWidth->InsertPreservingSize(nStartCol, nSize, STD_COL_WIDTH);
            // The inserted columns have the same widths as the columns, which were selected for insert.
            for (SCSIZE i=0; i < std::min(rDocument.MaxCol()-nSize-nStartCol, nSize); ++i)
                mpColWidth->SetValue(nStartCol + i, mpColWidth->GetValue(nStartCol+i+nSize));
            mpColFlags->InsertPreservingSize(nStartCol, nSize, CRFlags::NONE);
        }
        if (pOutlineTable)
            pOutlineTable->InsertCol( nStartCol, nSize );

        mpHiddenCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));
        mpFilteredCols->insertSegment(nStartCol, static_cast<SCCOL>(nSize));

        if (!maColManualBreaks.empty())
        {
            // Copy all breaks up to nStartCol (non-inclusive).
            ::std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
            ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);

            // Copy all breaks from nStartCol (inclusive) to the last element,
            // but add nSize to each value.
            ::std::set<SCCOL>::iterator itr2 = maColManualBreaks.end();
            for (; itr1 != itr2; ++itr1)
                aNewBreaks.insert(static_cast<SCCOL>(*itr1 + nSize));

            maColManualBreaks.swap(aNewBreaks);
        }
    }

    // Make sure there are enough columns at the end.
    CreateColumnIfNotExists(std::min<SCCOL>(rDocument.MaxCol(), std::max(nStartCol, aCol.size()) + nSize - 1 ));
    if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
    {
        // Move existing columns back, this will swap last empty columns in the inserted place.
        for (SCCOL nCol = aCol.size() - 1 - nSize; nCol >= nStartCol; --nCol)
            aCol[nCol].SwapCol(aCol[nCol+nSize]);
    }
    else
    {
        for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
            aCol[aCol.size() - 1 - nSize - i].MoveTo(nStartRow, nEndRow, aCol[aCol.size() - 1 - i]);
    }

    std::vector<SCCOL> aRegroupCols;
    rRegroupCols.getColumns(nTab, aRegroupCols);
    std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));

    if (nStartCol>0)                        // copy old attributes
    {
        sal_uInt16 nWhichArray[2];
        nWhichArray[0] = ATTR_MERGE;
        nWhichArray[1] = 0;

        sc::CopyToDocContext aCxt(rDocument);
        for (SCSIZE i=0; i<nSize; i++)
        {
            aCol[nStartCol-1].CopyToColumn(aCxt, nStartRow, nEndRow, InsertDeleteFlags::ATTRIB,
                                                false, aCol[nStartCol+i] );
            aCol[nStartCol+i].RemoveFlags( nStartRow, nEndRow,
                                                ScMF::Hor | ScMF::Ver | ScMF::Auto );
            aCol[nStartCol+i].ClearItems( nStartRow, nEndRow, nWhichArray );
        }
    }

    mpCondFormatList->InsertCol(nTab, nStartRow, nEndRow, nStartCol, nSize);

    InvalidatePageBreaks();

    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

void ScTable::DeleteCol(
    const sc::ColumnSet& rRegroupCols, SCCOL nStartCol, SCROW nStartRow, SCROW nEndRow, SCSIZE nSize, bool* pUndoOutline )
{
    if (nStartRow==0 && nEndRow==rDocument.MaxRow())
    {
        if (mpColWidth && mpColFlags)
        {
            assert( nStartCol + nSize <= o3tl::make_unsigned(rDocument.MaxCol()+1) );    // moving 0 if ==rDocument.MaxCol()+1 is correct
            mpColWidth->RemovePreservingSize(nStartCol, nSize, STD_COL_WIDTH);
            mpColFlags->RemovePreservingSize(nStartCol, nSize, CRFlags::NONE);
        }
        if (pOutlineTable)
            if (pOutlineTable->DeleteCol( nStartCol, nSize ))
                if (pUndoOutline)
                    *pUndoOutline = true;

        SCCOL nRmSize = nStartCol + static_cast<SCCOL>(nSize);
        mpHiddenCols->removeSegment(nStartCol, nRmSize);
        mpFilteredCols->removeSegment(nStartCol, nRmSize);

        if (!maColManualBreaks.empty())
        {
            // Erase all manual breaks between nStartCol and nStartCol + nSize - 1 (inclusive).
            std::set<SCCOL>::iterator itr1 = maColManualBreaks.lower_bound(nStartCol);
            std::set<SCCOL>::iterator itr2 = maColManualBreaks.upper_bound(static_cast<SCCOL>(nStartCol + nSize - 1));
            maColManualBreaks.erase(itr1, itr2);

            // Copy all breaks from the 1st element up to nStartCol to the new container.
            itr1 = maColManualBreaks.lower_bound(nStartCol);
            ::std::set<SCCOL> aNewBreaks(maColManualBreaks.begin(), itr1);

            // Copy all breaks from nStartCol to the last element, but subtract each value by nSize.
            itr2 = maColManualBreaks.end();
            for (; itr1 != itr2; ++itr1)
                aNewBreaks.insert(static_cast<SCCOL>(*itr1 - nSize));

            maColManualBreaks.swap(aNewBreaks);
        }
    }

    for (SCCOL col = nStartCol; col <= ClampToAllocatedColumns(nStartCol + nSize - 1); ++col)
        aCol[col].DeleteArea(nStartRow, nEndRow, InsertDeleteFlags::ALL, false);

    if ((nStartRow == 0) && (nEndRow == rDocument.MaxRow()))
    {
        for (SCCOL nCol = nStartCol + nSize; nCol < aCol.size(); ++nCol)
            aCol[nCol].SwapCol(aCol[nCol - nSize]);
    }
    else
    {
        for (SCSIZE i=0; static_cast<SCCOL>(i+nSize)+nStartCol < aCol.size(); i++)
            aCol[nStartCol + nSize + i].MoveTo(nStartRow, nEndRow, aCol[nStartCol + i]);
    }

    std::vector<SCCOL> aRegroupCols;
    rRegroupCols.getColumns(nTab, aRegroupCols);
    std::for_each(aRegroupCols.begin(), aRegroupCols.end(), ColumnRegroupFormulaCells(aCol, nullptr));

    InvalidatePageBreaks();

    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

void ScTable::DeleteArea(
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, InsertDeleteFlags nDelFlag,
    bool bBroadcast, sc::ColumnSpanSet* pBroadcastSpans )
{
    if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
    if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
    if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
    {
        {   // scope for bulk broadcast
            ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
            for (SCCOL i = nCol1; i <= nCol2; i++)
                aCol[i].DeleteArea(nRow1, nRow2, nDelFlag, bBroadcast, pBroadcastSpans);
        }

        if( nDelFlag & InsertDeleteFlags::ATTRIB )
            mpCondFormatList->DeleteArea( nCol1, nRow1, nCol2, nRow2 );
    }

    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

void ScTable::DeleteSelection( InsertDeleteFlags nDelFlag, const ScMarkData& rMarkbool bBroadcast )
{
    {   // scope for bulk broadcast
        ScBulkBroadcast aBulkBroadcast( rDocument.GetBASM(), SfxHintId::ScDataChanged);
        for (SCCOL i=0; i < aCol.size(); i++)
            aCol[i].DeleteSelection(nDelFlag, rMark, bBroadcast);
    }

    ScRangeList aRangeList;
    rMark.FillRangeListWithMarks(&aRangeList, false);

    for (size_t i = 0; i < aRangeList.size(); ++i)
    {
        const ScRange & rRange = aRangeList[i];

        if((nDelFlag & InsertDeleteFlags::ATTRIB) && rRange.aStart.Tab() == nTab)
            mpCondFormatList->DeleteArea( rRange.aStart.Col(), rRange.aStart.Row(), rRange.aEnd.Col(), rRange.aEnd.Row() );
    }
    // TODO: In the future we may want to check if the table has been
    // really modified before setting the stream invalid.
    SetStreamValid(false);
}

// pTable = Clipboard
void ScTable::CopyToClip(
    sc::CopyToClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
    ScTable* pTable )
{
    if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
        return;

    //  copy content
    //local range names need to be copied first for formula cells
    if (!pTable->mpRangeName && mpRangeName)
        pTable->mpRangeName.reset( new ScRangeName(*mpRangeName) );

    nCol2 = ClampToAllocatedColumns(nCol2);

    pTable->CreateColumnIfNotExists(nCol2);  // prevent repeated resizing
    for ( SCCOL i = nCol1; i <= nCol2; i++)
        aCol[i].CopyToClip(rCxt, nRow1, nRow2, pTable->CreateColumnIfNotExists(i));  // notes are handled at column level

    //  copy widths/heights, and only "hidden", "filtered" and "manual" flags
    //  also for all preceding columns/rows, to have valid positions for drawing objects

    if (mpColWidth && pTable->mpColWidth)
        pTable->mpColWidth->CopyFrom(*mpColWidth, 0, nCol2);

    pTable->CopyColHidden(*this, 0, nCol2);
    pTable->CopyColFiltered(*this, 0, nCol2);
    if (pDBDataNoName)
        pTable->SetAnonymousDBData(std::unique_ptr<ScDBData>(new ScDBData(*pDBDataNoName)));

    if (pRowFlags && pTable->pRowFlags && mpRowHeights && pTable->mpRowHeights)
    {
        pTable->pRowFlags->CopyFromAnded( *pRowFlags, 0, nRow2, CRFlags::ManualSize);
        pTable->CopyRowHeight(*this, 0, nRow2, 0);
    }

    pTable->CopyRowHidden(*this, 0, nRow2);
    pTable->CopyRowFiltered(*this, 0, nRow2);

    // If necessary replace formulas with values

    if ( IsProtected() )
        for (SCCOL i = nCol1; i <= nCol2; i++)
            pTable->aCol[i].RemoveProtected(nRow1, nRow2);

    mpCondFormatList->startRendering();
    mpCondFormatList->updateValues();
    pTable->mpCondFormatList.reset(mpCondFormatList->Clone(pTable->rDocument));
    mpCondFormatList->endRendering();
}

void ScTable::CopyToClip(
    sc::CopyToClipContext& rCxt, const ScRangeList& rRanges, ScTable* pTable )
{
    for ( size_t i = 0, nListSize = rRanges.size(); i < nListSize; ++i )
    {
        const ScRange & r = rRanges[ i ];
        CopyToClip( rCxt, r.aStart.Col(), r.aStart.Row(), r.aEnd.Col(), r.aEnd.Row(), pTable);
    }
}

void ScTable::CopyStaticToDocument(
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, const SvNumberFormatterMergeMap& rMap, ScTable* pDestTab )
{
    if (nCol1 > nCol2 || nRow1 > nRow2)
        return;

    const SCCOL nFirstUnallocated = std::clamp<SCCOL>(GetAllocatedColumnsCount(), nCol1, nCol2 + 1);
    if (nFirstUnallocated > nCol1)
        pDestTab->CreateColumnIfNotExists(nFirstUnallocated - 1);

    for (SCCOL i = nCol1; i < nFirstUnallocated; ++i)
    {
        ScColumn& rSrcCol = aCol[i];
        ScColumn& rDestCol = pDestTab->aCol[i];
        rSrcCol.CopyStaticToDocument(nRow1, nRow2, rMap, rDestCol);
    }

    // Maybe copy this table's default attrs to dest not limiting to already allocated in dest?
    const SCCOL nLastInDest = std::min<SCCOL>(pDestTab->GetAllocatedColumnsCount() - 1, nCol2);
    for (SCCOL i = nFirstUnallocated; i <= nLastInDest; ++i)
    {
        ScColumn& rDestCol = pDestTab->aCol[i];
        rDestCol.maCellTextAttrs.set_empty(nRow1, nRow2);
        rDestCol.maCells.set_empty(nRow1, nRow2);
        for (SCROW nRow = nRow1; nRow <= nRow2; ++nRow)
        {
            sal_uInt32 nNumFmt = aDefaultColData.GetPattern(nRow)->GetNumberFormat(
                rDocument.GetNonThreadedContext());
            SvNumberFormatterMergeMap::const_iterator itNum = rMap.find(nNumFmt);
            if (itNum != rMap.end())
                nNumFmt = itNum->second;

            rDestCol.SetNumberFormat(nRow, nNumFmt);
        }
        rDestCol.CellStorageModified();
    }
}

void ScTable::CopyCellToDocument(SCCOL nSrcCol, SCROW nSrcRow, SCCOL nDestCol, SCROW nDestRow, ScTable& rDestTab )
{
    if (!ValidColRow(nSrcCol, nSrcRow) || !ValidColRow(nDestCol, nDestRow))
        return;

    if (nSrcCol >= GetAllocatedColumnsCount())
    {
        if (nDestCol < rDestTab.GetAllocatedColumnsCount())
        {
            ScColumn& rDestCol = rDestTab.aCol[nDestCol];
            rDestCol.maCells.set_empty(nDestRow, nDestRow);
            rDestCol.maCellTextAttrs.set_empty(nDestRow, nDestRow);
            rDestCol.maCellNotes.set_empty(nDestRow, nDestRow);
            rDestCol.CellStorageModified();
        }
        return;
    }

    ScColumn& rSrcCol = aCol[nSrcCol];
    ScColumn& rDestCol = rDestTab.CreateColumnIfNotExists(nDestCol);
    rSrcCol.CopyCellToDocument(nSrcRow, nDestRow, rDestCol);
}

namespace {

bool isFormatDependentOnRange(const ScConditionalFormat& rFormat)
{
    for (size_t i = 0; i < rFormat.size(); ++i)
        if (auto* entry = rFormat.GetEntry(i))
            if (auto type = entry->GetType(); type == ScFormatEntry::Type::Colorscale
                                              || type == ScFormatEntry::Type::Databar
                                              || type == ScFormatEntry::Type::Iconset)
                return true;
    return false;
}

bool isRangeDependentFormatNeedDeduplication(const ScRangeList& rOld, const ScRangeList& rNew)
{
    // Are they two adjacent vectors?
    if (rOld.size() == 1 && rNew.size() == 1)
    {
        // Test vertical vectors
        if (rOld[0].aStart.Col() == rOld[0].aEnd.Col() && rNew[0].aStart.Col() == rNew[0].aEnd.Col()
            && rNew[0].aStart.Col() == rOld[0].aStart.Col())
        {
            if (rOld[0].aEnd.Row() == rNew[0].aStart.Row() - 1
                || rNew[0].aEnd.Row() == rOld[0].aStart.Row() - 1)
            {
                return true// Two joining vertical vectors -> merge
            }
        }
        // Test horizontal vectors
        if (rOld[0].aStart.Row() == rOld[0].aEnd.Row() && rNew[0].aStart.Row() == rNew[0].aEnd.Row()
            && rNew[0].aStart.Row() == rOld[0].aStart.Row())
        {
            if (rOld[0].aEnd.Col() == rNew[0].aStart.Col() - 1
                || rNew[0].aEnd.Col() == rOld[0].aStart.Col() - 1)
            {
                return true// Two joining horizontal vectors -> merge
            }
        }
    }

    // Is the new one fully included into the old one?
    for (auto& range : rNew)
        if (!rOld.Contains(range))
            return false// Different ranges, no deduplication

    return true// New is completely inside old -> merge (in fact, this means "nothing to do")
}

bool CheckAndDeduplicateCondFormat(ScDocument& rDocument, ScConditionalFormat* pOldFormat, const ScConditionalFormat* pNewFormat, SCTAB nTab)
{
    if (!pOldFormat)
        return false;

    if (pOldFormat->EqualEntries(*pNewFormat, true))
    {
        const ScRangeList& rNewRangeList = pNewFormat->GetRange();
        ScRangeList& rDstRangeList = pOldFormat->GetRangeList();

        if (isFormatDependentOnRange(*pOldFormat)
            && !isRangeDependentFormatNeedDeduplication(rDstRangeList, rNewRangeList))
            return false// No deduplication, create new format

        for (size_t i = 0; i < rNewRangeList.size(); ++i)
        {
            rDstRangeList.Join(rNewRangeList[i]);
        }
        rDocument.AddCondFormatData(rNewRangeList, nTab, pOldFormat->GetKey());
        return true;
    }

    return false;
}

}

void ScTable::CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
        SCCOL nDx, SCROW nDy, const ScTable* pTable)
{
    ScRange aOldRange( nCol1 - nDx, nRow1 - nDy, pTable->nTab, nCol2 - nDx, nRow2 - nDy, pTable->nTab);
    ScRange aNewRange( nCol1, nRow1, nTab, nCol2, nRow2, nTab );
    // Don't deduplicate when undoing or creating an Undo document! It would disallow correct undo
    bool bUndoContext = rDocument.IsUndo() || pTable->rDocument.IsUndo();
    // Note that Undo documents use same pool as the original document
    bool bSameDoc = rDocument.GetStyleSheetPool() == pTable->rDocument.GetStyleSheetPool();

    for(const auto& rxCondFormat : *pTable->mpCondFormatList)
    {
        const ScRangeList& rCondFormatRange = rxCondFormat->GetRange();
        if(!rCondFormatRange.Intersects( aOldRange ))
            continue;

        ScRangeList aIntersectedRange = rCondFormatRange.GetIntersectedRange(aOldRange);
        std::unique_ptr<ScConditionalFormat> pNewFormat = rxCondFormat->Clone(&rDocument);

        pNewFormat->SetRange(aIntersectedRange);
        sc::RefUpdateContext aRefCxt(rDocument);
        aRefCxt.meMode = URM_COPY;
        aRefCxt.maRange = aNewRange;
        aRefCxt.mnColDelta = nDx;
        aRefCxt.mnRowDelta = nDy;
        aRefCxt.mnTabDelta = nTab - pTable->nTab;
        pNewFormat->UpdateReference(aRefCxt, true);

        if (!bUndoContext && bSameDoc && pTable->nTab == nTab && CheckAndDeduplicateCondFormat(rDocument, mpCondFormatList->GetFormat(rxCondFormat->GetKey()), pNewFormat.get(), nTab))
        {
            continue;
        }
        sal_uInt32 nMax = 0;
        bool bDuplicate = false;
        for(const auto& rxCond : *mpCondFormatList)
        {
            // Check if there is the same format in the destination
            // If there is, then simply expand its range
            if (!bUndoContext && CheckAndDeduplicateCondFormat(rDocument, rxCond.get(), pNewFormat.get(), nTab))
            {
                bDuplicate = true;
                break;
            }

            if (rxCond->GetKey() > nMax)
                nMax = rxCond->GetKey();
        }
        // Do not add duplicate entries
        if (bDuplicate)
        {
            continue;
        }

        pNewFormat->SetKey(nMax + 1);
        auto pNewFormatTmp = pNewFormat.get();
        mpCondFormatList->InsertNew(std::move(pNewFormat));

        if(!bSameDoc)
        {
            for(size_t i = 0, n = pNewFormatTmp->size();
                    i < n; ++i)
            {
                OUString aStyleName;
                const ScFormatEntry* pEntry = pNewFormatTmp->GetEntry(i);
                if(pEntry->GetType() == ScFormatEntry::Type::Condition ||
                   pEntry->GetType() == ScFormatEntry::Type::ExtCondition)
                    aStyleName = static_cast<const ScCondFormatEntry*>(pEntry)->GetStyle();
                else if(pEntry->GetType() == ScFormatEntry::Type::Date)
                    aStyleName = static_cast<const ScCondDateFormatEntry*>(pEntry)->GetStyleName();

                if(!aStyleName.isEmpty())
                    rDocument.GetStyleSheetPool()->CopyStyleFrom(
                            pTable->rDocument.GetStyleSheetPool(), aStyleName, SfxStyleFamily::Para, true );
            }
        }

        rDocument.AddCondFormatData( pNewFormatTmp->GetRange(), nTab, pNewFormatTmp->GetKey() );
    }
}

bool ScTable::InitColumnBlockPosition( sc::ColumnBlockPosition& rBlockPos, SCCOL nCol )
{
    if (!ValidCol(nCol))
        return false;

    CreateColumnIfNotExists(nCol).InitBlockPosition(rBlockPos);
    return true;
}

// pTable is source

void ScTable::CopyFromClip(
    sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
    SCCOL nDx, SCROW nDy, ScTable* pTable )
{
    if (nCol2 > rDocument.MaxCol())
        nCol2 = rDocument.MaxCol();
    if (nRow2 > rDocument.MaxRow())
        nRow2 = rDocument.MaxRow();

    if (!(ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)))
        return;

    CreateColumnIfNotExists(nCol2);
    for ( SCCOL i = nCol1; i <= nCol2; i++)
    {
        pTable->CreateColumnIfNotExists(i - nDx);
        aCol[i].CopyFromClip(rCxt, nRow1, nRow2, nDy, pTable->aCol[i - nDx]); // notes are handles at column level
    }

    if (rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB)
    {
        // make sure that there are no old references to the cond formats
        sal_uInt16 nWhichArray[2];
        nWhichArray[0] = ATTR_CONDITIONAL;
        nWhichArray[1] = 0;
        for ( SCCOL i = nCol1; i <= nCol2; ++i)
            aCol[i].ClearItems(nRow1, nRow2, nWhichArray);
    }

    if ((rCxt.getInsertFlag() & InsertDeleteFlags::ATTRIB) == InsertDeleteFlags::NONE)
        return;

    if (nRow1==0 && nRow2==rDocument.MaxRow() && mpColWidth && pTable->mpColWidth)
        mpColWidth->CopyFrom(*pTable->mpColWidth, nCol1, nCol2, nCol1 - nDx);

    if (nCol1==0 && nCol2==rDocument.MaxCol() && mpRowHeights && pTable->mpRowHeights &&
                                     pRowFlags && pTable->pRowFlags)
    {
        CopyRowHeight(*pTable, nRow1, nRow2, -nDy);
        // Must copy CRFlags::ManualSize bit too, otherwise pRowHeight doesn't make sense
        for (SCROW j=nRow1; j<=nRow2; j++)
        {
            if ( pTable->pRowFlags->GetValue(j-nDy) & CRFlags::ManualSize )
                pRowFlags->OrValue( j, CRFlags::ManualSize);
            else
                pRowFlags->AndValue( j, ~CRFlags::ManualSize);
        }
    }
    // create deep copies for conditional formatting
    CopyConditionalFormat( nCol1, nRow1, nCol2, nRow2, nDx, nDy, pTable);
}

void ScTable::MixData(
    sc::MixDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
    ScPasteFunc nFunction, bool bSkipEmpty, const ScTable* pSrcTab )
{
    for (SCCOL nCol : pSrcTab->GetAllocatedColumnsRange(nCol1, nCol2))
        aCol[nCol].MixData(rCxt, nRow1, nRow2, nFunction, bSkipEmpty, pSrcTab->aCol[nCol]);
}

// Selection form this document
void ScTable::MixMarked(
    sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction,
    bool bSkipEmpty, const ScTable* pSrcTab )
{
    for (SCCOL i=0; i < aCol.size(); i++)
        aCol[i].MixMarked(rCxt, rMark, nFunction, bSkipEmpty, pSrcTab->aCol[i]);
}

namespace {

class TransClipHandler
{
    ScTable& mrClipTab;
    const ScTable& mrSrcTab;
    SCTAB mnSrcTab;
    SCCOL mnCol1;
    SCCOL mnSrcCol;
    size_t mnTopRow;
    size_t mnEndRow;
    SCROW mnTransRow;
    SCROW mnFilteredRows = 0;
    SCROW mnRowDestOffset = 0;
    bool mbAsLink;
    bool mbWasCut;
    bool mbIncludeFiltered;
    InsertDeleteFlags mnFlags;

    ScAddress getDestPos(size_t nRow) const
    {
        return ScAddress(static_cast<SCCOL>(mnCol1 + nRow - mnTopRow), mnTransRow,
                         mrClipTab.GetTab());
    }

    ScFormulaCell* createRefCell(size_t nSrcRow, const ScAddress& rDestPos) const
    {
        ScAddress aSrcPos(mnSrcCol, nSrcRow, mnSrcTab);
        ScSingleRefData aRef;
        aRef.InitAddress(aSrcPos); // Absolute reference.
        aRef.SetFlag3D(true);

        ScTokenArray aArr(mrClipTab.GetDoc());
        aArr.AddSingleReference(aRef);
        return new ScFormulaCell(mrClipTab.GetDoc(), rDestPos, aArr);
    }

    void setLink(size_t nRow)
    {
        SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
        mrClipTab.SetFormulaCell(nTransCol, mnTransRow,
                                 createRefCell(nRow, getDestPos(nRow)));
    }

public:
    TransClipHandler(ScTable& rClipTab, const ScTable& rSrcTab, SCTAB nSrcTab, SCCOL nCol1,
                     SCCOL nSrcCol, size_t nTopRow, size_t nEndRow, SCROW nCombinedStartRow,
                     SCROW nRowDestOffset, bool bAsLink, bool bWasCut,
                     const InsertDeleteFlags& nFlags, const bool bIncludeFiltered,
                     std::vector<SCROW>& rFilteredRows)
        : mrClipTab(rClipTab)
        , mrSrcTab(rSrcTab)
        , mnSrcTab(nSrcTab)
        , mnCol1(nCol1)
        , mnSrcCol(nSrcCol)
        , mnTopRow(nTopRow)
        , mnEndRow(nEndRow)
        , mnTransRow(nSrcCol - nCol1 + nCombinedStartRow)
        , mnRowDestOffset(nRowDestOffset)
        , mbAsLink(bAsLink)
        , mbWasCut(bWasCut)
        , mbIncludeFiltered(bIncludeFiltered)
        , mnFlags(nFlags)
    {
        // Create list of filtered rows.
        if (!mbIncludeFiltered)
        {
            for (SCROW curRow = nTopRow; curRow <= static_cast<SCROW>(mnEndRow); ++curRow)
            {
                // maybe this loop could be optimized
                bool bFiltered = mrSrcTab.RowFiltered(curRow, nullptr, nullptr);
                if (bFiltered)
                    rFilteredRows.push_back(curRow);
            }
        }
    }

    void operator() (size_t nRow, double fVal)
    {
        bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
        if (!mbIncludeFiltered && bFiltered)
        {
            mnFilteredRows++;
            return;
        }

        if (mbAsLink)
        {
            setLink(nRow);
            return;
        }

        SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
        mrClipTab.SetValue(nTransCol, mnTransRow, fVal);
    }

    void operator() (size_t nRow, const svl::SharedString& rStr)
    {
        bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
        if (!mbIncludeFiltered && bFiltered)
        {
            mnFilteredRows++;
            return;
        }

        if (mbAsLink)
        {
            setLink(nRow);
            return;
        }

        SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
        mrClipTab.SetRawString(nTransCol, mnTransRow, rStr);
    }

    void operator() (size_t nRow, const EditTextObject* p)
    {
        bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
        if (!mbIncludeFiltered && bFiltered)
        {
            mnFilteredRows++;
            return;
        }

        if (mbAsLink)
        {
            setLink(nRow);
            return;
        }

        SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
        mrClipTab.SetEditText(nTransCol, mnTransRow, ScEditUtil::Clone(*p, mrClipTab.GetDoc()));
    }

    void operator() (size_t nRow, const ScFormulaCell* p)
    {
        bool bFiltered = mrSrcTab.RowFiltered(nRow, nullptr, nullptr);
        if (!mbIncludeFiltered && bFiltered)
        {
            mnFilteredRows++;
            return;
        }

        if (mbAsLink)
        {
            setLink(nRow);
            return;
        }

        ScFormulaCell* pNew = new ScFormulaCell(*p, mrClipTab.GetDoc(),
                                                getDestPos(nRow - mnFilteredRows + mnRowDestOffset),
                                                ScCloneFlags::StartListening);

        //  rotate reference
        //  for Cut, the references are later adjusted through UpdateTranspose

        if (!mbWasCut)
            pNew->TransposeReference();

        SCCOL nTransCol = mnCol1 + nRow - mnTopRow - mnFilteredRows + mnRowDestOffset;
        mrClipTab.SetFormulaCell(nTransCol, mnTransRow, pNew);
    }

    // empty cells
    void operator()(const int /*type*/, size_t nRow, size_t nDataSize)
    {
        for (size_t curRow = nRow; curRow < nRow + nDataSize; ++curRow)
        {
            bool bFiltered = mrSrcTab.RowFiltered(curRow, nullptr, nullptr);
            if (!mbIncludeFiltered && bFiltered)
            {
                mnFilteredRows++;
                continue;
            }

            if (mbAsLink && mnFlags == InsertDeleteFlags::ALL)
            {
                //  with InsertDeleteFlags::ALL, also create links (formulas) for empty cells
                setLink(nRow);
                continue;
            }
        }
    }
};
}

void ScTable::TransposeClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
                            SCROW nCombinedStartRow, SCROW nRowDestOffset, ScTable* pTransClip,
                            InsertDeleteFlags nFlags, bool bAsLink, bool bIncludeFiltered)
{
    bool bWasCut = rDocument.IsCutMode();

    for (SCCOL nCol : GetWritableColumnsRange(nCol1, nCol2))
    {
        std::vector<SCROW> aFilteredRows;

        TransClipHandler aFunc(*pTransClip, *this, nTab, nCol1, nCol, nRow1, nRow2,
                               nCombinedStartRow, nRowDestOffset, bAsLink, bWasCut, nFlags,
                               bIncludeFiltered, aFilteredRows);

        const sc::CellStoreType& rCells = aCol[nCol].maCells;

        // Loop through all rows by iterator and call aFunc operators
        sc::ParseAll(rCells.begin(), rCells, nRow1, nRow2, aFunc,
                     aFunc);

        //  Attributes
        if (nFlags & InsertDeleteFlags::ATTRIB)
            TransposeColPatterns(pTransClip, nCol1, nCol, nRow1, nRow2, nCombinedStartRow,
                                 bIncludeFiltered, aFilteredRows, nRowDestOffset);

        // Cell Notes - fdo#68381 paste cell notes on Transpose
        if ((nFlags & InsertDeleteFlags::NOTE) && rDocument.HasColNotes(nCol, nTab))
            TransposeColNotes(pTransClip, nCol1, nCol, nRow1, nRow2, nCombinedStartRow,
                              bIncludeFiltered, nRowDestOffset);
    }
}

static void lcl_SetTransposedPatternInRows(ScTable* pTransClip, SCROW nAttrRow1, SCROW nAttrRow2,
                                           SCCOL nCol1, SCROW nRow1, SCROW nCombinedStartRow, SCCOL nCol,
                                           const CellAttributeHolder& rPatternHolder, bool bIncludeFiltered,
                                           const std::vector<SCROW>& rFilteredRows,
                                           SCROW nRowDestOffset)
{
    for (SCROW nRow = nAttrRow1; nRow <= nAttrRow2; nRow++)
    {
        size_t nFilteredRowAdjustment = 0;
        if (!bIncludeFiltered)
        {
            // aFilteredRows is sorted thus lower_bound() can be used.
            // lower_bound() has a logarithmic complexity O(log(n))
            auto itRow1 = std::lower_bound(rFilteredRows.begin(), rFilteredRows.end(), nRow1);
            auto itRow = std::lower_bound(rFilteredRows.begin(), rFilteredRows.end(), nRow);
            bool bRefRowIsFiltered = itRow != rFilteredRows.end() && *itRow == nRow;
            if (bRefRowIsFiltered)
                continue;

            // How many filtered rows are between the formula cell and the reference?
            // distance() has a constant complexity O(1) for vectors
            nFilteredRowAdjustment = std::distance(itRow1, itRow);
        }

        pTransClip->SetPattern(
            static_cast<SCCOL>(nCol1 + nRow - nRow1 - nFilteredRowAdjustment + nRowDestOffset),
            static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), rPatternHolder);
    }
}

void ScTable::TransposeColPatterns(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1,
                                   SCROW nRow2, SCROW nCombinedStartRow, bool bIncludeFiltered,
                                   const std::vector<SCROW>& rFilteredRows, SCROW nRowDestOffset)
{
    SCROW nAttrRow1 = {}; // spurious -Werror=maybe-uninitialized
    SCROW nAttrRow2 = {}; // spurious -Werror=maybe-uninitialized
    const ScPatternAttr* pPattern;
    ScAttrIterator aAttrIter(aCol[nCol].CreateAttrIterator( nRow1, nRow2 ));
    while ( (pPattern = aAttrIter.Next( nAttrRow1, nAttrRow2 )) != nullptr )
    {
            // ptr compare OK, was so before
            if (&rDocument.getCellAttributeHelper().getDefaultCellAttribute() != pPattern)
            {
                const SfxItemSet& rSet = pPattern->GetItemSet();
                if ( rSet.GetItemState( ATTR_MERGE, false ) == SfxItemState::DEFAULT &&
                     rSet.GetItemState( ATTR_MERGE_FLAG, false ) == SfxItemState::DEFAULT &&
                     rSet.GetItemState( ATTR_BORDER, false ) == SfxItemState::DEFAULT )
                {
                    // Set pattern in cells from nAttrRow1 to nAttrRow2
                    // no borders or merge items involved - use pattern as-is
                    const CellAttributeHolder aPatternHolder(pPattern);
                    lcl_SetTransposedPatternInRows(pTransClip, nAttrRow1, nAttrRow2, nCol1, nRow1,
                                                   nCombinedStartRow, nCol, aPatternHolder,
                                                   bIncludeFiltered, rFilteredRows, nRowDestOffset);
                }
                else
                {
                    // transpose borders and merge values, remove merge flags (refreshed after pasting)
                    ScPatternAttr aNewPattern( *pPattern );
                    SfxItemSet& rNewSet = aNewPattern.GetItemSet();

                    const SvxBoxItem& rOldBox = rSet.Get(ATTR_BORDER);
                    if ( rOldBox.GetTop() || rOldBox.GetBottom() || rOldBox.GetLeft() || rOldBox.GetRight() )
                    {
                        SvxBoxItem aNew( ATTR_BORDER );
                        aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
                        aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
                        aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
                        aNew.SetLine( rOldBox.GetLine( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
                        aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::TOP ), SvxBoxItemLine::LEFT );
                        aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::LEFT ), SvxBoxItemLine::TOP );
                        aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::BOTTOM ), SvxBoxItemLine::RIGHT );
                        aNew.SetDistance( rOldBox.GetDistance( SvxBoxItemLine::RIGHT ), SvxBoxItemLine::BOTTOM );
                        rNewSet.Put( aNew );
                    }

                    const ScMergeAttr& rOldMerge = rSet.Get(ATTR_MERGE);
                    if (rOldMerge.IsMerged())
                        rNewSet.Put( ScMergeAttr( std::min(
                                        static_cast<SCCOL>(rOldMerge.GetRowMerge()),
                                        static_cast<SCCOL>(rDocument.MaxCol()+1 - (nAttrRow2-nRow1))),
                                    std::min(
                                        static_cast<SCROW>(rOldMerge.GetColMerge()),
                                        static_cast<SCROW>(rDocument.MaxRow()+1 - (nCol-nCol1)))));
                    const ScMergeFlagAttr& rOldFlag = rSet.Get(ATTR_MERGE_FLAG);
                    if (rOldFlag.IsOverlapped())
                    {
                        ScMF nNewFlags = rOldFlag.GetValue() & ~ScMF( ScMF::Hor | ScMF::Ver );
                        if ( nNewFlags != ScMF::NONE )
                            rNewSet.Put( ScMergeFlagAttr( nNewFlags ) );
                        else
                            rNewSet.ClearItem( ATTR_MERGE_FLAG );
                    }

                    // Set pattern in cells from nAttrRow1 to nAttrRow2
                    const CellAttributeHolder aPatternHolder(&aNewPattern);
                    lcl_SetTransposedPatternInRows(pTransClip, nAttrRow1, nAttrRow2, nCol1, nRow1,
                                                   nCombinedStartRow, nCol, aPatternHolder,
                                                   bIncludeFiltered, rFilteredRows, nRowDestOffset);
                }
            }
    }
}

void ScTable::TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1,
                                SCROW nRow2, SCROW nCombinedStartRow, bool bIncludeFiltered,
                                SCROW nRowDestOffset)
{
    sc::CellNoteStoreType::const_iterator itBlk = aCol[nCol].maCellNotes.begin(), itBlkEnd = aCol[nCol].maCellNotes.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, nBlockStart = nBlockEnd)
    {
        nBlockEnd = nBlockStart + itBlk->size;
        if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
        {
            // Found.
            nOffsetInBlock = nRowPos - nBlockStart;
            break;
        }
    }

    if (itBlk == itBlkEnd)
        // Specified range found
        return;

    nRowPos = static_cast<size_t>(nRow2); // End row position.
    SCCOL nFilteredRows = 0;

    // Keep processing until we hit the end row position.
    sc::cellnote_block::const_iterator itData, itDataEnd;
    for (; itBlk != itBlkEnd; ++itBlk, nBlockStart = nBlockEnd, nOffsetInBlock = 0)
    {
        nBlockEnd = nBlockStart + itBlk->size;

        if (itBlk->data)
        {
            itData = sc::cellnote_block::begin(*itBlk->data);
            std::advance(itData, nOffsetInBlock);

            // selected area is smaller than the iteration block
            if (nBlockStart <= nRowPos && nRowPos < nBlockEnd)
            {
                // This block contains the end row. Only process partially.
                size_t nOffsetEnd = nRowPos - nBlockStart + 1;
                itDataEnd = sc::cellnote_block::begin(*itBlk->data);
                std::advance(itDataEnd, nOffsetEnd);
                size_t curRow = nBlockStart + nOffsetInBlock;
                for (; itData != itDataEnd; ++itData, ++curRow)
                {
                    bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
                    if (!bIncludeFiltered && bFiltered)
                    {
                        nFilteredRows++;
                        continue;
                    }

                    ScAddress aDestPos(
                        static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
                        static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
                    pTransClip->rDocument.ReleaseNote(aDestPos);
                    ScPostIt* pNote = *itData;
                    if (pNote)
                    {
                        std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), pTransClip->rDocument, aDestPos, true );
                        pTransClip->rDocument.SetNote(aDestPos, std::move(pClonedNote));
                    }
                }
                break// we reached the last valid block
            }
            else
            {
                itDataEnd = sc::cellnote_block::end(*itBlk->data);
                size_t curRow = nBlockStart + nOffsetInBlock;
                for (; itData != itDataEnd; ++itData, ++curRow)
                {
                    bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
                    if (!bIncludeFiltered && bFiltered)
                    {
                        nFilteredRows++;
                        continue;
                    }

                    ScAddress aDestPos(
                        static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
                        static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
                    pTransClip->rDocument.ReleaseNote(aDestPos);
                    ScPostIt* pNote = *itData;
                    if (pNote)
                    {
                        std::unique_ptr<ScPostIt> pClonedNote = pNote->Clone( ScAddress(nCol, curRow, nTab), pTransClip->rDocument, aDestPos, true );
                        pTransClip->rDocument.SetNote(aDestPos, std::move(pClonedNote));
                    }
                }
            }
        }
        else // remove dest notes for rows without notes
        {
            for (size_t curRow = nBlockStart + nOffsetInBlock;
                 curRow <= nBlockEnd && curRow <= nRowPos; ++curRow)
            {
                bool bFiltered = this->RowFiltered(curRow, nullptr, nullptr);
                if (!bIncludeFiltered && bFiltered && curRow < nBlockEnd)
                {
                    nFilteredRows++;
                    continue;
                }

                ScAddress aDestPos(
                    static_cast<SCCOL>(nCol1 + curRow - nRow1 - nFilteredRows + nRowDestOffset),
                    static_cast<SCROW>(nCombinedStartRow + nCol - nCol1), pTransClip->nTab);
                pTransClip->rDocument.ReleaseNote(aDestPos);
            }
        }
    }
}

ScColumn* ScTable::FetchColumn( SCCOL nCol )
{
    if (!ValidCol(nCol))
        return nullptr;

    return &CreateColumnIfNotExists(nCol);
}

const ScColumn* ScTable::FetchColumn( SCCOL nCol ) const
{
    if (!ValidCol(nCol) || nCol >= GetAllocatedColumnsCount())
        return nullptr;

    return &aCol[nCol];
}

void ScTable::StartListeners( sc::StartListeningContext& rCxt, bool bAll )
{
    std::shared_ptr<const sc::ColumnSet> pColSet = rCxt.getColumnSet();
    if (!pColSet)
    {
        for (SCCOL i=0; i < aCol.size(); i++)
            aCol[i].StartListeners(rCxt, bAll);
    }
    else if (pColSet->hasTab( nTab))
    {
        std::vector<SCCOL> aColumns;
        pColSet->getColumns( nTab, aColumns);
        for (auto i : aColumns)
        {
            if (0 <= i && i < aCol.size())
                aCol[i].StartListeners(rCxt, bAll);
        }
    }
}

void ScTable::AttachFormulaCells(
    sc::StartListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    nCol2 = ClampToAllocatedColumns(nCol2);
    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
        aCol[nCol].AttachFormulaCells(rCxt, nRow1, nRow2);
}

void ScTable::DetachFormulaCells(
    sc::EndListeningContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    nCol2 = ClampToAllocatedColumns(nCol2);
    for (SCCOL nCol = nCol1; nCol <= nCol2; ++nCol)
        aCol[nCol].DetachFormulaCells(rCxt, nRow1, nRow2);
}

void ScTable::SetDirtyFromClip(
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, sc::ColumnSpanSet& rBroadcastSpans )
{
    if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
    if (nCol2 > rDocument.MaxCol()) nCol2 = rDocument.MaxCol();
    if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
    if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
        for (SCCOL i = nCol1; i <= nCol2; i++)
            aCol[i].SetDirtyFromClip(nRow1, nRow2, rBroadcastSpans);
}

void ScTable::StartListeningFormulaCells(
    sc::StartListeningContext& rStartCxt, sc::EndListeningContext& rEndCxt,
    SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 )
{
    if ( nCol2 >= aCol.size() ) nCol2 = aCol.size() - 1;
    if (nCol2 > rDocument.MaxCol()) nCol2 = rDocument.MaxCol();
    if (nRow2 > rDocument.MaxRow()) nRow2 = rDocument.MaxRow();
    if (ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2))
        for (SCCOL i = nCol1; i <= nCol2; i++)
            aCol[i].StartListeningFormulaCells(rStartCxt, rEndCxt, nRow1, nRow2);
}

void ScTable::CopyToTable(
    sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
    InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab, const ScMarkData* pMarkData,
    bool bAsLink, bool bColRowFlags, bool bGlobalNamesToLocal, bool bCopyCaptions )
{
    if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
        return;

    const bool bToUndoDoc = pDestTab->rDocument.IsUndo();
    const bool bFromUndoDoc = rDocument.IsUndo();

    if (bToUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB) && nCol2 >= aCol.size())
    {
        // tdf#154044: Copy also the default column data
        aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
                                             pDestTab->aDefaultColData.AttrArray());
    }

    if ((bToUndoDoc || bFromUndoDoc) && (nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName)
    {
        // Copying formulas may create sheet-local named expressions on the
        // destination sheet. Add existing to Undo first.
        // During Undo restore the previous named expressions.
        pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
        if (!pDestTab->rDocument.IsClipOrUndo())
        {
            ScDocShell* pDocSh = pDestTab->rDocument.GetDocumentShell();
            if (pDocSh)
                pDocSh->SetAreasChangedNeedBroadcast();
        }
    }

    if (nFlags != InsertDeleteFlags::NONE)
    {
        InsertDeleteFlags nTempFlags( nFlags &
                ~InsertDeleteFlags( InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES));
        // tdf#102364 - in some pathological cases CopyToTable() replacing cells with new cells
        // can lead to repetitive splitting and rejoining of the same formula group, which can get
        // quadratically expensive with large groups. So do the grouping just once at the end.
        sc::DelayFormulaGroupingSwitch delayGrouping( pDestTab->rDocument, true );
        pDestTab->CreateColumnIfNotExists(ClampToAllocatedColumns(nCol2)); // avoid repeated resizing
        for (SCCOL i = nCol1; i <= ClampToAllocatedColumns(nCol2); i++)
            aCol[i].CopyToColumn(rCxt, nRow1, nRow2, bToUndoDoc ? nFlags : nTempFlags, bMarked,
                                 pDestTab->CreateColumnIfNotExists(i), pMarkData, bAsLink, bGlobalNamesToLocal);
        // tdf#154044: Restore from the default column data
        if (bFromUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB) && nCol2 >= aCol.size())
        {
            aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
                                                 pDestTab->aDefaultColData.AttrArray());
            SCCOL nMaxSetDefault = pDestTab->ClampToAllocatedColumns(nCol2);
            for (SCCOL i = aCol.size(); i <= nMaxSetDefault; i++)
                aDefaultColData.AttrArray().CopyArea(nRow1, nRow2, 0,
                                                     pDestTab->aCol[i].AttrArray());
        }
    }

    if (!bColRowFlags)      // Column widths/Row heights/Flags
        return;

    if (bToUndoDoc && (nFlags & InsertDeleteFlags::ATTRIB))
    {
        pDestTab->mpCondFormatList.reset(mpCondFormatList->Clone(pDestTab->rDocument));
    }

    if (pDBDataNoName)
    {
        std::unique_ptr<ScDBData> pNewDBData(new ScDBData(*pDBDataNoName));
        SCCOL aCol1, aCol2;
        SCROW aRow1, aRow2;
        SCTAB aTab;
        pNewDBData->GetArea(aTab, aCol1, aRow1, aCol2, aRow2);
        pNewDBData->MoveTo(pDestTab->nTab, aCol1, aRow1, aCol2, aRow2);
        pDestTab->SetAnonymousDBData(std::move(pNewDBData));
    }
    //  Charts have to be adjusted when hide/show
    ScChartListenerCollection* pCharts = pDestTab->rDocument.GetChartListenerCollection();

    bool bFlagChange = false;

    if (nRow1 == 0 && nRow2 == rDocument.MaxRow() && mpColWidth && pDestTab->mpColWidth)
    {
        auto destTabColWidthIt = pDestTab->mpColWidth->begin() + nCol1;
        auto thisTabColWidthIt = mpColWidth->begin() + nCol1;
        pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
        pDestTab->mpColFlags->CopyFrom(*mpColFlags, nCol1, nCol2);
        for (SCCOL i = nCol1; i <= nCol2; ++i)
        {
            bool bThisHidden = ColHidden(i);
            bool bHiddenChange = (pDestTab->ColHidden(i) != bThisHidden);
            bool bChange = bHiddenChange || (*destTabColWidthIt != *thisTabColWidthIt);
            pDestTab->SetColHidden(i, i, bThisHidden);
            //TODO: collect changes?
            if (bHiddenChange && pCharts)
                pCharts->SetRangeDirty(ScRange(i, 0, nTab, i, rDocument.MaxRow(), nTab));

            if (bChange)
                bFlagChange = true;

            ++destTabColWidthIt;
            ++thisTabColWidthIt;
        }
        pDestTab->SetColManualBreaks(std::set(maColManualBreaks));
    }

    if (nCol1 == 0 && nCol2 == rDocument.MaxCol() && mpRowHeights && pDestTab->mpRowHeights)
    {
        bool bChange = pDestTab->GetRowHeight(nRow1, nRow2) != GetRowHeight(nRow1, nRow2);

        if (bChange)
            bFlagChange = true;

        pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
        pDestTab->pRowFlags->CopyFrom(*pRowFlags, nRow1, nRow2);

        // Hidden flags.
        for (SCROW i = nRow1; i <= nRow2; ++i)
        {
            SCROW nLastRow;
            bool bHidden = RowHidden(i, nullptr, &nLastRow);
            if (nLastRow >= nRow2)
                // the last row shouldn't exceed the upper bound the caller specified.
                nLastRow = nRow2;

            bool bHiddenChanged = pDestTab->SetRowHidden(i, nLastRow, bHidden);
            if (bHiddenChanged && pCharts)
                // Hidden flags differ.
                pCharts->SetRangeDirty(ScRange(0, i, nTab, rDocument.MaxCol(), nLastRow, nTab));

            if (bHiddenChanged)
                bFlagChange = true;

            // Jump to the last row of the identical flag segment.
            i = nLastRow;
        }

        // Filtered flags.
        for (SCROW i = nRow1; i <= nRow2; ++i)
        {
            SCROW nLastRow;
            bool bFiltered = RowFiltered(i, nullptr, &nLastRow);
            if (nLastRow >= nRow2)
                // the last row shouldn't exceed the upper bound the caller specified.
                nLastRow = nRow2;
            pDestTab->SetRowFiltered(i, nLastRow, bFiltered);
            i = nLastRow;
        }
        pDestTab->SetRowManualBreaks(std::set(maRowManualBreaks));
    }

    if (bFlagChange)
        pDestTab->InvalidatePageBreaks();

    if(nFlags & InsertDeleteFlags::ATTRIB)
    {
        pDestTab->mpCondFormatList->DeleteArea(nCol1, nRow1, nCol2, nRow2);
        pDestTab->CopyConditionalFormat(nCol1, nRow1, nCol2, nRow2, 0, 0, this);
    }

    if(nFlags & InsertDeleteFlags::OUTLINE) // also only when bColRowFlags
        pDestTab->SetOutlineTable( pOutlineTable.get() );

    if (nFlags & InsertDeleteFlags::SPARKLINES)
    {
        CopySparklinesToTable(nCol1, nRow1, nCol2, nRow2, pDestTab);
    }

    if (!bToUndoDoc && bCopyCaptions && (nFlags & (InsertDeleteFlags::NOTE | InsertDeleteFlags::ADDNOTES)))
    {
        bool bCloneCaption = (nFlags & InsertDeleteFlags::NOCAPTIONS) == InsertDeleteFlags::NONE;
        CopyCaptionsToTable( nCol1, nRow1, nCol2, nRow2, pDestTab, bCloneCaption);
    }
}

void ScTable::CopySparklinesToTable(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab)
{
    if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
        return;

    nCol2 = ClampToAllocatedColumns(nCol2);
    for (SCCOL i = nCol1; i <= nCol2; i++)
    {
        aCol[i].CopyCellSparklinesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i));
    }
}

void ScTable::CopyCaptionsToTable( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, ScTable* pDestTab,
        bool bCloneCaption )
{
    if (!ValidColRow(nCol1, nRow1) || !ValidColRow(nCol2, nRow2))
        return;

    nCol2 = ClampToAllocatedColumns(nCol2);
    for (SCCOL i = nCol1; i <= nCol2; i++)
    {
        aCol[i].CopyCellNotesToDocument(nRow1, nRow2, pDestTab->CreateColumnIfNotExists(i), bCloneCaption);
        pDestTab->aCol[i].UpdateNoteCaptions(nRow1, nRow2, false /* address unchanged from initial create */);
    }
}

void ScTable::UndoToTable(
    sc::CopyToDocContext& rCxt, SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
    InsertDeleteFlags nFlags, bool bMarked, ScTable* pDestTab )
{
    if (!(ValidColRow(nCol1, nRow1) && ValidColRow(nCol2, nRow2)))
        return;

    bool bWidth  = (nRow1==0 && nRow2==rDocument.MaxRow() && mpColWidth && pDestTab->mpColWidth);
    bool bHeight = (nCol1==0 && nCol2==rDocument.MaxCol() && mpRowHeights && pDestTab->mpRowHeights);

    if ((nFlags & InsertDeleteFlags::CONTENTS) && mpRangeName)
    {
        // Undo sheet-local named expressions created during copying
        // formulas. If mpRangeName is not set then the Undo wasn't even
        // set to an empty ScRangeName map so don't "undo" that.
        pDestTab->SetRangeName( std::unique_ptr<ScRangeName>( new ScRangeName( *GetRangeName())));
        if (!pDestTab->rDocument.IsClipOrUndo())
        {
            ScDocShell* pDocSh = pDestTab->rDocument.GetDocumentShell();
            if (pDocSh)
                pDocSh->SetAreasChangedNeedBroadcast();
        }

    }

    for ( SCCOL i = 0; i < aCol.size(); i++)
    {
        auto& rDestCol = pDestTab->CreateColumnIfNotExists(i);
        if ( i >= nCol1 && i <= nCol2 )
            aCol[i].UndoToColumn(rCxt, nRow1, nRow2, nFlags, bMarked, rDestCol);
        else
            aCol[i].CopyToColumn(rCxt, 0, rDocument.MaxRow(), InsertDeleteFlags::FORMULA, falserDestCol);
    }

    if (nFlags & InsertDeleteFlags::ATTRIB)
        pDestTab->mpCondFormatList.reset(mpCondFormatList->Clone(pDestTab->rDocument));

    if (!(bWidth||bHeight))
        return;

    if (bWidth)
    {
        pDestTab->mpColWidth->CopyFrom(*mpColWidth, nCol1, nCol2);
        pDestTab->SetColManualBreaks( std::set(maColManualBreaks) );
    }
    if (bHeight)
    {
        pDestTab->CopyRowHeight(*this, nRow1, nRow2, 0);
        pDestTab->SetRowManualBreaks( std::set(maRowManualBreaks) );
    }
}

void ScTable::CopyUpdated( const ScTable* pPosTab, ScTable* pDestTab ) const
{
    pDestTab->CreateColumnIfNotExists(aCol.size()-1);
    for (SCCOL i=0; i < aCol.size(); i++)
        aCol[i].CopyUpdated( pPosTab->FetchColumn(i), pDestTab->aCol[i] );
}

void ScTable::InvalidateTableArea()
{
    bTableAreaValid = false;
    bTableAreaVisibleValid = false;
}

void ScTable::InvalidatePageBreaks()
{
    mbPageBreaksValid = false;
}

void ScTable::CopyScenarioTo( ScTable* pDestTab ) const
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    for (SCCOL i=0; i < aCol.size(); i++)
        aCol[i].CopyScenarioTo( pDestTab->CreateColumnIfNotExists(i) );
}

void ScTable::CopyScenarioFrom( const ScTable* pSrcTab )
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    SCCOL nEndCol = pSrcTab->aCol.size();
    CreateColumnIfNotExists(nEndCol);
    for (SCCOL i=0; i < nEndCol; i++)
        aCol[i].CopyScenarioFrom( pSrcTab->aCol[i] );
}

void ScTable::MarkScenarioIn( ScMarkData& rDestMark, ScScenarioFlags nNeededBits ) const
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    if ( ( nScenarioFlags & nNeededBits ) != nNeededBits )  // Are all Bits set?
        return;

    for (SCCOL i=0; i < aCol.size(); i++)
        aCol[i].MarkScenarioIn( rDestMark );
}

bool ScTable::HasScenarioRange( const ScRange& rRange ) const
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    ScRange aTabRange = rRange;
    aTabRange.aStart.SetTab( nTab );
    aTabRange.aEnd.SetTab( nTab );

    const ScRangeList* pList = GetScenarioRanges();

    if (pList)
    {
        for ( size_t j = 0, n = pList->size(); j < n; j++ )
        {
            const ScRange & rR = (*pList)[j];
            if ( rR.Intersects( aTabRange ) )
                return true;
        }
    }

    return false;
}

void ScTable::InvalidateScenarioRanges()
{
    pScenarioRanges.reset();
}

const ScRangeList* ScTable::GetScenarioRanges() const
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    if (!pScenarioRanges)
    {
        const_cast<ScTable*>(this)->pScenarioRanges.reset(new ScRangeList);
        ScMarkData aMark(rDocument.GetSheetLimits());
        MarkScenarioIn( aMark, ScScenarioFlags::NONE );     // always
        aMark.FillRangeListWithMarks( pScenarioRanges.get(), false );
    }
    return pScenarioRanges.get();
}

bool ScTable::TestCopyScenarioTo( const ScTable* pDestTab ) const
{
    OSL_ENSURE( bScenario, "bScenario == FALSE" );

    if (!pDestTab->IsProtected())
        return true;

    bool bOk = true;
    for (SCCOL i=0; i < aCol.size() && bOk; i++)
        bOk = aCol[i].TestCopyScenarioTo( pDestTab->aCol[i] );
    return bOk;
}

bool ScTable::SetString( SCCOL nCol, SCROW nRow, SCTAB nTabP, const OUString& rString,
                         const ScSetStringParam * pParam )
{
    if (!ValidColRow(nCol,nRow))
    {
        return false;
    }

    return CreateColumnIfNotExists(nCol).SetString(
        nRow, nTabP, rString, rDocument.GetAddressConvention(), pParam);
}

bool ScTable::SetEditText( SCCOL nCol, SCROW nRow, std::unique_ptr<EditTextObject> pEditText )
{
    if (!ValidColRow(nCol, nRow))
    {
        return false;
    }

    CreateColumnIfNotExists(nCol).SetEditText(nRow, std::move(pEditText));
    return true;
}

void ScTable::SetEditText( SCCOL nCol, SCROW nRow, const EditTextObject& rEditText, const SfxItemPool* pEditPool )
{
    if (!ValidColRow(nCol, nRow))
        return;

    CreateColumnIfNotExists(nCol).SetEditText(nRow, rEditText, pEditPool);
}

SCROW ScTable::GetFirstEditTextRow( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2 ) const
{
    if (!ValidCol(nCol1) || !ValidCol(nCol2) || nCol2 < nCol1)
        return -1;

    if (!ValidRow(nRow1) || !ValidRow(nRow2) || nRow2 < nRow1)
        return -1;

    nCol2 = ClampToAllocatedColumns(nCol2);
    SCROW nFirst = rDocument.MaxRow()+1;
    for (SCCOL i = nCol1; i <= nCol2; ++i)
    {
        const ScColumn& rCol = aCol[i];
--> --------------------

--> maximum size reached

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

Messung V0.5
C=92 H=93 G=92

¤ Dauer der Verarbeitung: 0.19 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.