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

Quelle  ucalc_copypaste.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/.
 */


#include "helper/debughelper.hxx"
#include "helper/qahelper.hxx"

#include <attrib.hxx>
#include <bcaslot.hxx>
#include <clipparam.hxx>
#include <dbdata.hxx>
#include <docfunc.hxx>
#include <editeng/borderline.hxx>
#include <editeng/brushitem.hxx>
#include <editutil.hxx>
#include <formulacell.hxx>
#include <iostream>
#include <patattr.hxx>
#include <postit.hxx>
#include <queryparam.hxx>
#include <refundo.hxx>
#include <scitems.hxx>
#include <scopetools.hxx>

#include <sfx2/docfile.hxx>

#include <memory>

using ::std::cerr;
using ::std::endl;

class TestCopyPaste : public ScUcalcTestBase
{
protected:
    enum CalcMode
    {
        NoCalc,
        AutoCalc,
        RecalcAtEnd,
        HardRecalcAtEnd
    };

    void executeCopyPasteSpecial(bool bApplyFilter, bool bIncludedFiltered, bool bAsLink,
                                 bool bTranspose, bool bMultiRangeSelection, bool bSkipEmpty,
                                 bool bCut = false,
                                 ScClipParam::Direction eDirection = ScClipParam::Column,
                                 CalcMode eCalcMode = CalcMode::AutoCalc,
                                 InsertDeleteFlags aFlags
                                 = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB);
    void executeCopyPasteSpecial(const SCTAB srcSheet, const SCTAB destSheet, bool bApplyFilter,
                                 bool bIncludedFiltered, bool bAsLink, bool bTranspose,
                                 bool bMultiRangeSelection, bool bSkipEmpty,
                                 std::unique_ptr<ScUndoCut>& pUndoCut,
                                 std::unique_ptr<ScUndoPaste>& pUndoPaste, bool bCut = false,
                                 ScClipParam::Direction eDirection = ScClipParam::Column,
                                 CalcMode eCalcMode = CalcMode::AutoCalc,
                                 InsertDeleteFlags aFlags
                                 = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB);
    void checkCopyPasteSpecialInitial(const SCTAB srcSheet);
    void checkCopyPasteSpecial(bool bSkipEmpty, bool bCut = false);
    void checkCopyPasteSpecialFiltered(bool bSkipEmpty);
    void checkCopyPasteSpecialTranspose(bool bSkipEmpty, bool bCut = false);
    void checkCopyPasteSpecialFilteredTranspose(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeCol(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeColFiltered(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeColTranspose(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeColFilteredTranspose(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeRow(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeRowFiltered(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeRowTranspose(bool bSkipEmpty);
    void checkCopyPasteSpecialMultiRangeRowFilteredTranspose(bool bSkipEmpty);
    void checkReferencedCutTransposedRangesRowUndo(const SCTAB nSrcTab, const SCTAB nDestTab);
    void executeReferencedCutRangesRow(const bool bTransposed, const SCTAB nSrcTab,
                                       const SCTAB nDestTab, const bool bUndo,
                                       std::unique_ptr<ScUndoCut>& pUndoCut,
                                       std::unique_ptr<ScUndoPaste>& pUndoPaste);
    void checkReferencedCutRangesRowIntitial(const SCTAB nSrcTab, const OUString& rDesc);
    void checkReferencedCutRangesRow(const SCTAB nSrcTab, const SCTAB nDestTab);
    void checkReferencedCutTransposedRangesRow(const SCTAB nSrcTab, const SCTAB nDestTab);
    void executeReferencedCutRangesCol(const bool bTransposed, const SCTAB nSrcTab,
                                       const SCTAB nDestTab, const bool bUndo,
                                       std::unique_ptr<ScUndoCut>& pUndoCut,
                                       std::unique_ptr<ScUndoPaste>& pUndoPaste);
    void checkReferencedCutRangesColIntitial(const SCTAB nSrcTab, const SCTAB nDestTab,
                                             const OUString& rDesc);
    void checkReferencedCutRangesCol(const SCTAB nSrcTab, const SCTAB nDestTab);
    void checkReferencedCutTransposedRangesColUndo(const SCTAB nSrcTab, const SCTAB nDestTab);
    void checkReferencedCutTransposedRangesCol(const SCTAB nSrcTab, const SCTAB nDestTab);
    void prepareUndoBeforePaste(bool bCut, ScDocumentUniquePtr& pPasteUndoDoc,
                                std::unique_ptr<ScDocument>& pPasteRefUndoDoc,
                                const ScMarkData& rDestMark, const ScRange& rDestRange,
                                std::unique_ptr<ScRefUndoData>& pUndoData);
    void prepareUndoAfterPaste(ScDocumentUniquePtr& pPasteUndoDoc,
                               std::unique_ptr<ScDocument>& pPasteRefUndoDoc,
                               const ScMarkData& rDestMark, const ScRange& rDestRange,
                               std::unique_ptr<ScRefUndoData>& pUndoData,
                               std::unique_ptr<ScUndoPaste>& pUndoPaste, bool bTranspose = false,
                               bool bAsLink = falsebool bSkipEmpty = false,
                               ScPasteFunc nFunction = ScPasteFunc::NONE,
                               InsCellCmd eMoveMode = InsCellCmd::INS_NONE);

    void printValuesAndFormulasInRange(ScDocument* pDoc, const ScRange& rRange,
                                       const OString& rCaption);
    OUString getRangeByName(const OUString& aRangeName);
    ScAddress setNote(SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& noteText);
    OUString getNote(SCCOL nCol, SCROW nRow, SCTAB nTab);
};

static ScMF lcl_getMergeFlagOfCell(const ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab)
{
    const SfxPoolItem& rPoolItem = rDoc.GetPattern(nCol, nRow, nTab)->GetItem(ATTR_MERGE_FLAG);
    const ScMergeFlagAttr& rMergeFlag = static_cast<const ScMergeFlagAttr&>(rPoolItem);
    return rMergeFlag.GetValue();
}

static ScAddress lcl_getMergeSizeOfCell(const ScDocument& rDoc, SCCOL nCol, SCROW nRow, SCTAB nTab)
{
    const SfxPoolItem& rPoolItem = rDoc.GetPattern(nCol, nRow, nTab)->GetItem(ATTR_MERGE);
    const ScMergeAttr& rMerge = static_cast<const ScMergeAttr&>(rPoolItem);
    return ScAddress(rMerge.GetColMerge(), rMerge.GetRowMerge(), nTab);
}

void TestCopyPaste::printValuesAndFormulasInRange(ScDocument* pDoc, const ScRange&&nbsp;rRange,
                                                  const OString& rCaption)
{
    printRange(pDoc, rRange, rCaption, false);
    printRange(pDoc, rRange, rCaption, true);
}

OUString TestCopyPaste::getRangeByName(const OUString& aRangeName)
{
    return ScUcalcTestBase::getRangeByName(m_pDoc, aRangeName);
}

ScAddress TestCopyPaste::setNote(SCCOL nCol, SCROW nRow, SCTAB nTab, const OUString& noteText)
{
    ScAddress aAdr(nCol, nRow, nTab);
    ScPostIt* pNote = m_pDoc->GetOrCreateNote(aAdr);
    pNote->SetText(aAdr, noteText);
    return aAdr;
}

OUString TestCopyPaste::getNote(SCCOL nCol, SCROW nRow, SCTAB nTab)
{
    ScPostIt* pNote = m_pDoc->GetNote(nCol, nRow, nTab);
    CPPUNIT_ASSERT_MESSAGE("Note expected", pNote);
    return pNote->GetText();
}

// Cannot be moved to qahelper since ScDocument::CopyToDocument() is not SC_DLLPUBLIC
/** Executes the same steps for undo as ScViewFunc::PasteFromClip(). */
void TestCopyPaste::prepareUndoBeforePaste(bool bCut, ScDocumentUniquePtr& pPasteUndoDoc,
                                           std::unique_ptr<ScDocument>& pPasteRefUndoDoc,
                                           const ScMarkData& rDestMark, const ScRange& rDestRange,
                                           std::unique_ptr<ScRefUndoData>& pUndoData)
{
    InsertDeleteFlags nUndoFlags = InsertDeleteFlags::CONTENTS;
    SCTAB nTabCount = m_pDoc->GetTableCount();

    pPasteUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
    pPasteUndoDoc->InitUndoSelected(*m_pDoc, rDestMark, falsefalse);
    // all sheets - CopyToDocument skips those that don't exist in pUndoDoc
    m_pDoc->CopyToDocument(rDestRange.aStart.Col(), rDestRange.aStart.Row(), 0,
                           rDestRange.aEnd.Col(), rDestRange.aEnd.Row(), nTabCount - 1, nUndoFlags,
                           false, *pPasteUndoDoc);

    if (bCut)
    {
        // save changed references
        pPasteRefUndoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
        pPasteRefUndoDoc->InitUndo(*m_pDoc, 0, nTabCount - 1);

        pUndoData.reset(new ScRefUndoData(*m_pDoc));
    }
}

// Cannot be moved to qahelper since ScDocument::CopyToDocument() is not SC_DLLPUBLIC
/** Executes the same steps for undo as ScViewFunc::PasteFromClip(). */
void TestCopyPaste::prepareUndoAfterPaste(ScDocumentUniquePtr& pPasteUndoDoc,
                                          std::unique_ptr<ScDocument>& pPasteRefUndoDoc,
                                          const ScMarkData& rDestMark, const ScRange& rDestRange,
                                          std::unique_ptr<ScRefUndoData>& pUndoData,
                                          std::unique_ptr<ScUndoPaste>& pUndoPaste, bool bTranspose,
                                          bool bAsLink, bool bSkipEmpty, ScPasteFunc nFunction,
                                          InsCellCmd eMoveMode)
{
    InsertDeleteFlags nUndoFlags = InsertDeleteFlags::CONTENTS;
    SCTAB nTabCount = m_pDoc->GetTableCount();

    ScDocumentUniquePtr pPasteRedoDoc;
    // copy redo data after appearance of the first undo
    // don't create Redo-Doc without RefUndoDoc

    if (pPasteRefUndoDoc)
    {
        pPasteRedoDoc.reset(new ScDocument(SCDOCMODE_UNDO));
        pPasteRedoDoc->InitUndo(*m_pDoc, rDestRange.aStart.Tab(), rDestRange.aEnd.Tab(), false,
                                false);

        // move adapted refs to Redo-Doc

        pPasteRedoDoc->AddUndoTab(0, nTabCount - 1);
        m_pDoc->CopyUpdated(pPasteRefUndoDoc.get(), pPasteRedoDoc.get());

        pPasteUndoDoc->AddUndoTab(0, nTabCount - 1);
        pPasteRefUndoDoc->DeleteArea(rDestRange.aStart.Col(), rDestRange.aStart.Row(),
                                     rDestRange.aEnd.Col(), rDestRange.aEnd.Row(), rDestMark,
                                     InsertDeleteFlags::ALL);
        pPasteRefUndoDoc->CopyToDocument(0, 0, 0, pPasteUndoDoc->MaxCol(), pPasteUndoDoc->MaxRow(),
                                         nTabCount - 1, InsertDeleteFlags::FORMULA, false,
                                         *pPasteUndoDoc);
        pPasteRefUndoDoc.reset();
    }

    ScUndoPasteOptions aOptions; // store options for repeat
    aOptions.nFunction = nFunction;
    aOptions.bSkipEmptyCells = bSkipEmpty;
    aOptions.bTranspose = bTranspose;
    aOptions.bAsLink = bAsLink;
    aOptions.eMoveMode = eMoveMode;

    pUndoPaste.reset(new ScUndoPaste(*m_xDocShell, rDestRange, rDestMark, std::move(pPasteUndoDoc),
                                     std::move(pPasteRedoDoc), nUndoFlags, std::move(pUndoData),
                                     false,
                                     &aOptions)); // false = Redo data not yet copied
}

CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPaste)
{
    m_pDoc->InsertTab(0, u"Sheet1"_ustr);
    m_pDoc->InsertTab(1, u"Sheet2"_ustr);

    // We need a drawing layer in order to create caption objects.
    m_pDoc->InitDrawLayer(m_xDocShell.get());

    //test copy&paste + ScUndoPaste
    //copy local and global range names in formulas
    //string cells and value cells
    m_pDoc->SetValue(0, 0, 0, 1);
    m_pDoc->SetValue(3, 0, 0, 0);
    m_pDoc->SetValue(3, 1, 0, 1);
    m_pDoc->SetValue(3, 2, 0, 2);
    m_pDoc->SetValue(3, 3, 0, 3);
    m_pDoc->SetString(2, 0, 0, u"test"_ustr);
    ScAddress aAdr(0, 0, 0);

    //create some range names, local and global
    ScRangeData* pLocal1 = new ScRangeData(*m_pDoc, u"local1"_ustr, aAdr);
    ScRangeData* pLocal2 = new ScRangeData(*m_pDoc, u"local2"_ustr, aAdr);
    ScRangeData* pLocal3 = new ScRangeData(*m_pDoc, u"local3"_ustr, u"$Sheet1.$A$1"_ustr);
    ScRangeData* pLocal4 = new ScRangeData(*m_pDoc, u"local4"_ustr, u"Sheet1.$A$1"_ustr);
    ScRangeData* pLocal5 = new ScRangeData(*m_pDoc, u"local5"_ustr,
                                           u"$A$1"_ustr); // implicit relative sheet reference
    ScRangeData* pGlobal = new ScRangeData(*m_pDoc, u"global"_ustr, aAdr);
    static constexpr OUString aGlobal2Symbol(u"$Sheet1.$A$1:$A$23"_ustr);
    ScRangeData* pGlobal2 = new ScRangeData(*m_pDoc, u"global2"_ustr, aGlobal2Symbol);
    std::unique_ptr<ScRangeName> pGlobalRangeName(new ScRangeName());
    pGlobalRangeName->insert(pGlobal);
    pGlobalRangeName->insert(pGlobal2);
    std::unique_ptr<ScRangeName> pLocalRangeName1(new ScRangeName());
    pLocalRangeName1->insert(pLocal1);
    pLocalRangeName1->insert(pLocal2);
    pLocalRangeName1->insert(pLocal3);
    pLocalRangeName1->insert(pLocal4);
    pLocalRangeName1->insert(pLocal5);
    m_pDoc->SetRangeName(std::move(pGlobalRangeName));
    m_pDoc->SetRangeName(0, std::move(pLocalRangeName1));

    // Add formula to B1.
    OUString aFormulaString(u"=local1+global+SUM($C$1:$D$4)+local3+local4+local5"_ustr);
    m_pDoc->SetString(1, 0, 0, aFormulaString);

    double fValue = m_pDoc->GetValue(ScAddress(1, 0, 0));
    ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 11", 11, fValue);

    // add notes to A1:C1
    setNote(0, 0, 0, u"Hello world in A1"_ustr); // empty cell content
    setNote(1, 0, 0, u"Hello world in B1"_ustr); // formula cell content
    setNote(2, 0, 0, u"Hello world in C1"_ustr); // string cell content

    //copy Sheet1.A1:C1 to Sheet2.A2:C2
    ScRange aRange(0, 0, 0, 2, 0, 0);
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aRange, &aClipDoc);

    aRange = ScRange(0, 1, 1, 2, 1, 1); //target: Sheet2.A2:C2
    ScDocumentUniquePtr pUndoDoc(new ScDocument(SCDOCMODE_UNDO));
    pUndoDoc->InitUndo(*m_pDoc, 1, 1, truetrue);
    std::unique_ptr<ScUndoPaste> pUndo(createUndoPaste(*m_xDocShell, aRange, std::move(pUndoDoc)));
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aRange);
    m_pDoc->CopyFromClip(aRange, aMark, InsertDeleteFlags::ALL, nullptr, &aClipDoc);

    //check values after copying
    OUString aString = m_pDoc->GetFormula(1, 1, 1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("formula string was not copied correctly", aString,
                                 aFormulaString);
    // Only the global range points to Sheet1.A1, all copied sheet-local ranges
    // to Sheet2.A1 that is empty, hence the result is 1, not 2.
    fValue = m_pDoc->GetValue(ScAddress(1, 1, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("copied formula should return 1", 1.0, fValue);
    fValue = m_pDoc->GetValue(ScAddress(0, 1, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("copied value should be 1", 1.0, fValue);

    ScRange aSheet2A1(0, 0, 1, 0, 0, 1);

    //check local range name after copying
    pLocal1 = m_pDoc->GetRangeName(1)->findByUpperName(u"LOCAL1"_ustr);
    CPPUNIT_ASSERT_MESSAGE("local range name 1 should be copied", pLocal1);
    ScRange aRangeLocal1;
    bool bIsValidRef1 = pLocal1->IsValidReference(aRangeLocal1);
    CPPUNIT_ASSERT_MESSAGE("local range name 1 should be valid", bIsValidRef1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("local range 1 should now point to Sheet2.A1", aSheet2A1,
                                 aRangeLocal1);

    pLocal2 = m_pDoc->GetRangeName(1)->findByUpperName(u"LOCAL2"_ustr);
    CPPUNIT_ASSERT_MESSAGE("local2 should not be copied", !pLocal2);

    pLocal3 = m_pDoc->GetRangeName(1)->findByUpperName(u"LOCAL3"_ustr);
    CPPUNIT_ASSERT_MESSAGE("local range name 3 should be copied", pLocal3);
    ScRange aRangeLocal3;
    bool bIsValidRef3 = pLocal3->IsValidReference(aRangeLocal3);
    CPPUNIT_ASSERT_MESSAGE("local range name 3 should be valid", bIsValidRef3);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("local range 3 should now point to Sheet2.A1", aSheet2A1,
                                 aRangeLocal3);

    pLocal4 = m_pDoc->GetRangeName(1)->findByUpperName(u"LOCAL4"_ustr);
    CPPUNIT_ASSERT_MESSAGE("local range name 4 should be copied", pLocal4);
    ScRange aRangeLocal4;
    bool bIsValidRef4 = pLocal4->IsValidReference(aRangeLocal4);
    CPPUNIT_ASSERT_MESSAGE("local range name 4 should be valid", bIsValidRef4);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("local range 4 should now point to Sheet2.A1", aSheet2A1,
                                 aRangeLocal4);

    pLocal5 = m_pDoc->GetRangeName(1)->findByUpperName(u"LOCAL5"_ustr);
    CPPUNIT_ASSERT_MESSAGE("local range name 5 should be copied", pLocal5);
    ScRange aRangeLocal5;
    bool bIsValidRef5 = pLocal5->IsValidReference(aRangeLocal5);
    CPPUNIT_ASSERT_MESSAGE("local range name 5 should be valid", bIsValidRef5);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("local range 5 should now point to Sheet2.A1", aSheet2A1,
                                 aRangeLocal5);

    // check notes after copying
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.A2", m_pDoc->HasNote(0, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.B2", m_pDoc->HasNote(1, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.C2", m_pDoc->HasNote(2, 1, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "Note content on Sheet1.A1 not copied to Sheet2.A2, empty cell content",
        m_pDoc->GetNote(0, 0, 0)->GetText(), m_pDoc->GetNote(0, 1, 1)->GetText());
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "Note content on Sheet1.B1 not copied to Sheet2.B2, formula cell content",
        m_pDoc->GetNote(1, 0, 0)->GetText(), m_pDoc->GetNote(1, 1, 1)->GetText());
    CPPUNIT_ASSERT_EQUAL_MESSAGE(
        "Note content on Sheet1.C1 not copied to Sheet2.C2, string cell content",
        m_pDoc->GetNote(2, 0, 0)->GetText(), m_pDoc->GetNote(2, 1, 1)->GetText());

    //check undo and redo
    pUndo->Undo();
    fValue = m_pDoc->GetValue(ScAddress(1, 1, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("after undo formula should return nothing", 0, fValue);
    aString = m_pDoc->GetString(2, 1, 1);
    CPPUNIT_ASSERT_MESSAGE("after undo, string should be removed", aString.isEmpty());
    CPPUNIT_ASSERT_MESSAGE("after undo, note on A2 should be removed", !m_pDoc->HasNote(0, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("after undo, note on B2 should be removed", !m_pDoc->HasNote(1, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("after undo, note on C2 should be removed", !m_pDoc->HasNote(2, 1, 1));

    pUndo->Redo();
    fValue = m_pDoc->GetValue(ScAddress(1, 1, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("formula should return 1 after redo", 1.0, fValue);
    aString = m_pDoc->GetString(2, 1, 1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell Sheet2.C2 should contain: test", u"test"_ustr, aString);
    aString = m_pDoc->GetFormula(1, 1, 1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula should be correct again", aFormulaString, aString);

    CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.A2",
                           m_pDoc->HasNote(0, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.B2",
                           m_pDoc->HasNote(1, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("After Redo, there should be a note on Sheet2.C2",
                           m_pDoc->HasNote(2, 1, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("After Redo, note again on Sheet2.A2, empty cell content",
                                 getNote(0, 0, 0), getNote(0, 1, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("After Redo, note again on Sheet2.B2, formula cell content",
                                 getNote(1, 0, 0), getNote(1, 1, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("After Redo, note again on Sheet2.C2, string cell content",
                                 getNote(2, 0, 0), getNote(2, 1, 1));

    // Copy Sheet1.A11:A13 to Sheet1.A7:A9, both within global2 range.
    aRange = ScRange(0, 10, 0, 0, 12, 0);
    ScDocument aClipDoc2(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aRange, &aClipDoc2);

    aRange = ScRange(0, 6, 0, 0, 8, 0);
    aMark.SetMarkArea(aRange);
    m_pDoc->CopyFromClip(aRange, aMark, InsertDeleteFlags::ALL, nullptr, &aClipDoc2);

    // The global2 range must not have changed.
    pGlobal2 = m_pDoc->GetRangeName()->findByUpperName(u"GLOBAL2"_ustr);
    CPPUNIT_ASSERT_MESSAGE("GLOBAL2 name not found", pGlobal2);
    OUString aSymbol = pGlobal2->GetSymbol();
    CPPUNIT_ASSERT_EQUAL_MESSAGE("GLOBAL2 named range changed", aGlobal2Symbol, aSymbol);

    m_pDoc->DeleteTab(1);
    m_pDoc->DeleteTab(0);
}

CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteAsLink)
{
    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(0, u"Sheet1"_ustr);
    m_pDoc->InsertTab(1, u"Sheet2"_ustr);

    m_pDoc->SetValue(ScAddress(0, 0, 0), 1); // A1
    m_pDoc->SetValue(ScAddress(0, 1, 0), 2); // A2
    m_pDoc->SetValue(ScAddress(0, 2, 0), 3); // A3

    ScRange aRange(0, 0, 0, 0, 2, 0); // Copy A1:A3 to clip.
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aRange, &aClipDoc);

    aRange = ScRange(1, 1, 1, 1, 3, 1); // Paste to B2:B4 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aRange);
    // Paste range as link.
    m_pDoc->CopyFromClip(aRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, &aClipDoc, true,
                         true);

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 2, 1));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 3, 1));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL(3.0, pFC->GetValue());

    m_pDoc->DeleteTab(1);
    m_pDoc->DeleteTab(0);
}

CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteTranspose)
{
    m_pDoc->InsertTab(0, u"Sheet1"_ustr);

    // We need a drawing layer in order to create caption objects.
    m_pDoc->InitDrawLayer(m_xDocShell.get());

    m_pDoc->SetValue(0, 0, 0, 1);
    m_pDoc->SetString(1, 0, 0, u"=A1+1"_ustr);
    m_pDoc->SetString(2, 0, 0, u"test"_ustr);

    // add notes to A1:C1
    setNote(0, 0, 0, u"Hello world in A1"_ustr); // numerical cell content
    setNote(1, 0, 0, u"Hello world in B1"_ustr); // formula cell content
    setNote(2, 0, 0, u"Hello world in C1"_ustr); // string cell content

    // transpose clipboard, paste and check on Sheet2
    m_pDoc->InsertTab(1, u"Sheet2"_ustr);

    ScRange aSrcRange(0, 0, 0, 2, 0, 0);
    ScDocument aNewClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aNewClipDoc);

    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aNewClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, falsefalse);

    ScRange aDestRange(3, 1, 1, 3, 3, 1); //target: Sheet2.D2:D4
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pTransClip.get());
    pTransClip.reset();

    //check cell content after transposed copy/paste
    OUString aString = m_pDoc->GetString(3, 3, 1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell Sheet2.D4 should contain: test", u"test"_ustr, aString);
    double fValue = m_pDoc->GetValue(ScAddress(3, 1, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied cell should return 1", 1, fValue);
    fValue = m_pDoc->GetValue(ScAddress(3, 2, 1));
    ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied formula should return 2", 2, fValue);
    aString = m_pDoc->GetFormula(3, 2, 1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("transposed formula should point on Sheet2.D2", u"=D2+1"_ustr,
                                 aString);

    // check notes after transposed copy/paste
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D2", m_pDoc->HasNote(3, 1, 1));
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D3", m_pDoc->HasNote(3, 2, 1));
    CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D4", m_pDoc->HasNote(3, 3, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D2", getNote(0, 0, 0),
                                 getNote(3, 1, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D3", getNote(1, 0, 0),
                                 getNote(3, 2, 1));
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D4", getNote(2, 0, 0),
                                 getNote(3, 3, 1));

    m_pDoc->DeleteTab(1);
    m_pDoc->DeleteTab(0);
}

CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMergedCellsTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 2, srcSheet, 3); // A3
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    m_pDoc->DoMerge(0, 1, 1, 1, srcSheet, false); // Merge A2 and B2
    m_pDoc->DoMerge(0, 2, 1, 2, srcSheet, false); // Merge A3 and B3

    // Test precondition
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 0, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 0, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(2, 1, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::Hor, lcl_getMergeFlagOfCell(*m_pDoc, 1, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(2, 1, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::Hor, lcl_getMergeFlagOfCell(*m_pDoc, 1, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 3, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 3, srcSheet));

    ScRange aSrcRange(0, 0, srcSheet, 1, 3, srcSheet); // Copy A1:B4 to clip.
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, truefalse);

    ScRange aDestRange(1, 1, destSheet, 4, 2, destSheet); // Paste to B2:E3 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pTransClip.get(), true,
                         false);
    pTransClip.reset();

    // Check transpose of merged cells
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(1, 2, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, m_pDoc->GetValue(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(1, 2, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 4, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 4, 2, destSheet));

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMergedCellsFilteredTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 2, srcSheet, 3); // A3
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    m_pDoc->DoMerge(0, 1, 1, 1, srcSheet, false); // Merge A2 and B2
    m_pDoc->DoMerge(0, 2, 1, 2, srcSheet, false); // Merge A3 and B3

    // Filter row 1
    ScDBData* pDBData = new ScDBData(u"TRANSPOSE_TEST_DATA"_ustr, srcSheet, 0, 0, 0, 3);
    m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));

    pDBData->SetAutoFilter(true);
    ScRange aRange;
    pDBData->GetArea(aRange);
    m_pDoc->ApplyFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
                          aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto);

    //create the query param
    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    ScQueryEntry& rEntry = aParam.GetEntry(0);
    rEntry.bDoQuery = true;
    rEntry.nField = 0;
    rEntry.eOp = SC_NOT_EQUAL;
    rEntry.GetQueryItem().mfVal = 2; // value of row A2 -> filtering row 1
    // add queryParam to database range.
    pDBData->SetQueryParam(aParam);

    // perform the query.
    m_pDoc->Query(srcSheet, aParam, true);

    // Test precondition
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 0, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 0, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(2, 1, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::Hor, lcl_getMergeFlagOfCell(*m_pDoc, 1, 1, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(2, 1, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::Hor, lcl_getMergeFlagOfCell(*m_pDoc, 1, 2, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, srcSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 0, 3, srcSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 3, srcSheet));

    ScRange aSrcRange(0, 0, srcSheet, 1, 3, srcSheet); // Copy A1:B4 to clip.
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, truefalse);

    ScRange aDestRange(1, 1, destSheet, 3, 2, destSheet); // Paste to B2:D3 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pTransClip.get(), true,
                         false);
    pTransClip.reset();

    // Check transpose of merged cells
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(1, 2, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(3.0, m_pDoc->GetValue(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScAddress(0, 0, destSheet),
                         lcl_getMergeSizeOfCell(*m_pDoc, 3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(ScMF::NONE, lcl_getMergeFlagOfCell(*m_pDoc, 3, 2, destSheet));

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// InsertDeleteFlags::CONTENTS
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialAsLinkTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    ScRange aSrcRange(0, 0, srcSheet, 0, 3, srcSheet); // Copy A1:A4 to clip.
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, truefalse);

    ScRange aDestRange(1, 1, destSheet, 4, 1, destSheet); // Paste to B2:E2 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, pTransClip.get(),
                         truefalse);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$2"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell D2.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(4, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell E2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(4, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// InsertDeleteFlags::CONTENTS
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialAsLinkFilteredTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    // Filter row 1
    ScDBData* pDBData = new ScDBData(u"TRANSPOSE_TEST_DATA"_ustr, srcSheet, 0, 0, 0, 3);
    m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));

    pDBData->SetAutoFilter(true);
    ScRange aRange;
    pDBData->GetArea(aRange);
    m_pDoc->ApplyFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
                          aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto);

    //create the query param
    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    ScQueryEntry& rEntry = aParam.GetEntry(0);
    rEntry.bDoQuery = true;
    rEntry.nField = 0;
    rEntry.eOp = SC_NOT_EQUAL;
    rEntry.GetQueryItem().mfVal = 2; // value of row A2 -> filtering row 1
    // add queryParam to database range.
    pDBData->SetQueryParam(aParam);

    // perform the query.
    m_pDoc->Query(srcSheet, aParam, true);

    // Check precondition for test: row 1 is hidden/filtered
    SCROW nRow1, nRow2;
    SCROW nFilteredRow1, nFilteredRow2;
    bool bHidden = m_pDoc->RowHidden(SCROW(1), srcSheet, &nRow1, &nRow2);
    CPPUNIT_ASSERT_MESSAGE("row 1 should be hidden", bHidden);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be hidden", SCROW(1), nRow1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be hidden", SCROW(1), nRow2);
    bool bFiltered = m_pDoc->RowFiltered(SCROW(1), srcSheet, &nFilteredRow1, &nFilteredRow2);
    CPPUNIT_ASSERT_MESSAGE("row 1 should be filtered", bFiltered);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be filtered", SCROW(1), nFilteredRow1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be filtered", SCROW(1), nFilteredRow2);

    // Copy A1:A4 to clip.
    ScRange aSrcRange(0, 0, srcSheet, 0, 3, srcSheet);
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, truefalse);

    ScRange aDestRange(1, 1, destSheet, 3, 1, destSheet); // Paste to B2:D2 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::CONTENTS, nullptr, pTransClip.get(),
                         truefalsefalse);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell C2.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// tdf#141683
// InsertDeleteFlags::VALUE
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMultiRangeRowAsLinkTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(1, 0, srcSheet, 2); // B1
    m_pDoc->SetValue(3, 0, srcSheet, 4); // D1

    m_pDoc->SetValue(0, 2, srcSheet, 11); // A3
    m_pDoc->SetValue(1, 2, srcSheet, 12); // B3
    m_pDoc->SetValue(3, 2, srcSheet, 14); // D3

    ScMarkData aSrcMark(m_pDoc->GetSheetLimits());
    aSrcMark.SelectOneTable(0);
    ScClipParam aClipParam;
    aClipParam.meDirection = ScClipParam::Row;
    aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 3, 0, srcSheet)); // A1:D1
    aClipParam.maRanges.push_back(ScRange(0, 2, srcSheet, 3, 2, srcSheet)); // A3:D3

    ScDocument aClipDoc(SCDOCMODE_CLIP);
    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aSrcMark, falsefalse);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::VALUE, truefalse);

    ScRange aDestRange(1, 1, destSheet, 2, 4, destSheet); // Paste to B2:C5 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyMultiRangeFromClip(
        ScAddress(1, 1, destSheet), aMark, InsertDeleteFlags::VALUE | InsertDeleteFlags::FORMULA,
        pTransClip.get(), truefalse /* false fixes tdf#141683 */, false, false);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B3", u"=$Sheet1.$B$1"_ustr,
                                 m_pDoc->GetFormula(1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 3, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell B4.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 4, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B5", u"=$Sheet1.$D$1"_ustr,
                                 m_pDoc->GetFormula(1, 4, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$3"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(11.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C3", u"=$Sheet1.$B$3"_ustr,
                                 m_pDoc->GetFormula(2, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(12.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 3, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell C4.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 4, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C5", u"=$Sheet1.$D$3"_ustr,
                                 m_pDoc->GetFormula(2, 4, destSheet));
    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// tdf#141683
// InsertDeleteFlags::VALUE
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMultiRangeRowAsLinkFilteredTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(1, 0, srcSheet, 2); // B1
    m_pDoc->SetValue(3, 0, srcSheet, 4); // D1

    m_pDoc->SetValue(0, 1, srcSheet, -1); // A2, filtered and selected
    m_pDoc->SetValue(1, 1, srcSheet, -2); // B2, filtered and selected
    m_pDoc->SetValue(3, 1, srcSheet, -4); // D2, filtered and selected

    m_pDoc->SetValue(0, 2, srcSheet, 11); // A3
    m_pDoc->SetValue(1, 2, srcSheet, 12); // B3
    m_pDoc->SetValue(3, 2, srcSheet, 14); // D3

    m_pDoc->SetValue(0, 3, srcSheet, -11); // A4, filtered and not selected
    m_pDoc->SetValue(1, 3, srcSheet, -12); // B4, filtered and not selected
    m_pDoc->SetValue(3, 3, srcSheet, -14); // D4, filtered and not selected

    m_pDoc->SetValue(0, 5, srcSheet, 111); // A6
    m_pDoc->SetValue(1, 5, srcSheet, 112); // B6
    m_pDoc->SetValue(3, 5, srcSheet, 114); // D6

    // Filter row 1
    ScDBData* pDBData = new ScDBData(u"TRANSPOSE_TEST_DATA"_ustr, srcSheet, 0, 0, 3, 3);
    m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));

    pDBData->SetAutoFilter(true);
    ScRange aRange;
    pDBData->GetArea(aRange);
    m_pDoc->ApplyFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
                          aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto);

    //create the query param
    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    ScQueryEntry& rEntry = aParam.GetEntry(0);
    rEntry.bDoQuery = true;
    rEntry.nField = 0;
    rEntry.eOp = SC_GREATER_EQUAL;
    rEntry.GetQueryItem().mfVal = 0; // filtering negative values -> filtering row 1 and 2
    // add queryParam to database range.
    pDBData->SetQueryParam(aParam);

    // perform the query.
    m_pDoc->Query(srcSheet, aParam, true);

    ScMarkData aSrcMark(m_pDoc->GetSheetLimits());
    aSrcMark.SelectOneTable(0);
    ScClipParam aClipParam;
    aClipParam.meDirection = ScClipParam::Row;
    aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 3, 2, srcSheet)); // A1:C3
    aClipParam.maRanges.push_back(ScRange(0, 5, srcSheet, 3, 5, srcSheet)); // A6:C6

    ScDocument aClipDoc(SCDOCMODE_CLIP);
    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aSrcMark, falsefalse);

    printRange(m_pDoc, aClipParam.getWholeRange(), "Src range");
    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::VALUE, truefalse);

    printRange(&aClipDoc, ScRange(0, 0, 0, 4, 5, 0), "Base doc (&aClipDoc)");
    printRange(pTransClip.get(), ScRange(0, 0, 0, 3, 3, 0),
               "Transposed filtered clipdoc (pTransClip.get())");
    ScRange aDestRange(1, 1, destSheet, 3, 4, destSheet); // Paste to B2:D5 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyMultiRangeFromClip(
        ScAddress(1, 1, destSheet), aMark, InsertDeleteFlags::VALUE | InsertDeleteFlags::FORMULA,
        pTransClip.get(), truefalse /* false fixes tdf#141683 */, false, false);
    pTransClip.reset();
    printRange(m_pDoc, aDestRange, "Transposed dest sheet");

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B3", u"=$Sheet1.$B$1"_ustr,
                                 m_pDoc->GetFormula(1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 3, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell B4.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 4, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B5", u"=$Sheet1.$D$1"_ustr,
                                 m_pDoc->GetFormula(1, 4, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$3"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(11.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C3", u"=$Sheet1.$B$3"_ustr,
                                 m_pDoc->GetFormula(2, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(12.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 3, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell C4.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 4, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C5", u"=$Sheet1.$D$3"_ustr,
                                 m_pDoc->GetFormula(2, 4, destSheet));
    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell D2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D2", u"=$Sheet1.$A$6"_ustr,
                                 m_pDoc->GetFormula(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(111.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell D3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D3", u"=$Sheet1.$B$6"_ustr,
                                 m_pDoc->GetFormula(3, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(112.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 3, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell D4.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 4, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D5", u"=$Sheet1.$D$6"_ustr,
                                 m_pDoc->GetFormula(3, 4, destSheet));
    CPPUNIT_ASSERT_EQUAL(114.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// tdf#141683
// InsertDeleteFlags::VALUE
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMultiRangeColAsLinkTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    m_pDoc->SetValue(2, 0, srcSheet, 11); // C1
    m_pDoc->SetValue(2, 1, srcSheet, 12); // C2
    m_pDoc->SetValue(2, 3, srcSheet, 14); // C4

    ScMarkData aSrcMark(m_pDoc->GetSheetLimits());
    aSrcMark.SelectOneTable(0);
    ScClipParam aClipParam;
    aClipParam.meDirection = ScClipParam::Column;
    aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 0, 3, srcSheet)); // A1:A4
    aClipParam.maRanges.push_back(ScRange(2, 0, srcSheet, 2, 3, srcSheet)); // C1:C4

    ScDocument aClipDoc(SCDOCMODE_CLIP);
    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aSrcMark, falsefalse);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::VALUE, truefalse);

    ScRange aDestRange(1, 1, destSheet, 4, 2, destSheet); // Paste to B2:E3 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyMultiRangeFromClip(
        ScAddress(1, 1, destSheet), aMark, InsertDeleteFlags::VALUE | InsertDeleteFlags::FORMULA,
        pTransClip.get(), truefalse /* false fixes tdf#141683 */, false, false);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$2"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell D2.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(4, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell E2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(4, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B3", u"=$Sheet1.$C$1"_ustr,
                                 m_pDoc->GetFormula(1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(11.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell C3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C3", u"=$Sheet1.$C$2"_ustr,
                                 m_pDoc->GetFormula(2, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(12.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell D3.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(4, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell E3", u"=$Sheet1.$C$4"_ustr,
                                 m_pDoc->GetFormula(4, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// tdf#141683
// InsertDeleteFlags::VALUE
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialMultiRangeColAsLinkFilteredTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    m_pDoc->SetValue(2, 0, srcSheet, 11); // C1
    m_pDoc->SetValue(2, 1, srcSheet, 12); // C2
    m_pDoc->SetValue(2, 3, srcSheet, 14); // C4

    // Filter row 1
    ScDBData* pDBData = new ScDBData(u"TRANSPOSE_TEST_DATA"_ustr, srcSheet, 0, 0, 0, 3);
    m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));

    pDBData->SetAutoFilter(true);
    ScRange aRange;
    pDBData->GetArea(aRange);
    m_pDoc->ApplyFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
                          aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto);

    //create the query param
    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    ScQueryEntry& rEntry = aParam.GetEntry(0);
    rEntry.bDoQuery = true;
    rEntry.nField = 0;
    rEntry.eOp = SC_NOT_EQUAL;
    rEntry.GetQueryItem().mfVal = 2; // value of row A2 -> filtering row 1
    // add queryParam to database range.
    pDBData->SetQueryParam(aParam);

    // perform the query.
    m_pDoc->Query(srcSheet, aParam, true);

    ScMarkData aSrcMark(m_pDoc->GetSheetLimits());
    aSrcMark.SelectOneTable(0);
    ScClipParam aClipParam;
    aClipParam.meDirection = ScClipParam::Column;
    aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 0, 3, srcSheet)); // A1:A4
    aClipParam.maRanges.push_back(ScRange(2, 0, srcSheet, 2, 3, srcSheet)); // C1:C4

    ScDocument aClipDoc(SCDOCMODE_CLIP);
    m_pDoc->CopyToClip(aClipParam, &aClipDoc, &aSrcMark, falsefalse);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::VALUE, truefalse);

    ScRange aDestRange(1, 1, destSheet, 4, 2, destSheet); // Paste to B2:E3 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyMultiRangeFromClip(
        ScAddress(1, 1, destSheet), aMark, InsertDeleteFlags::VALUE | InsertDeleteFlags::FORMULA,
        pTransClip.get(), truefalse /* false fixes tdf#141683 */, false, false);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell C2.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(1, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B3.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B3", u"=$Sheet1.$C$1"_ustr,
                                 m_pDoc->GetFormula(1, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(11.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be no formula cell C3.", !pFC);

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 2, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D3", u"=$Sheet1.$C$4"_ustr,
                                 m_pDoc->GetFormula(3, 2, destSheet));
    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// InsertDeleteFlags::ALL
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialAllAsLinkTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    ScRange aSrcRange(0, 0, srcSheet, 0, 3, srcSheet); // Copy A1:A4 to clip.
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, truefalse);

    ScRange aDestRange(1, 1, destSheet, 4, 1, destSheet); // Paste to B2:E2 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pTransClip.get(), true,
                         false);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$2"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(2.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D2", u"=$Sheet1.$A$3"_ustr,
                                 m_pDoc->GetFormula(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(0.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(4, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell E2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(4, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// InsertDeleteFlags::ALL
CPPUNIT_TEST_FIXTURE(TestCopyPaste, testCopyPasteSpecialAllAsLinkFilteredTranspose)
{
    const SCTAB srcSheet = 0;
    const SCTAB destSheet = 1;

    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.

    m_pDoc->InsertTab(srcSheet, u"Sheet1"_ustr);
    m_pDoc->InsertTab(destSheet, u"Sheet2"_ustr);

    m_pDoc->SetValue(0, 0, srcSheet, 1); // A1
    m_pDoc->SetValue(0, 1, srcSheet, 2); // A2
    m_pDoc->SetValue(0, 3, srcSheet, 4); // A4

    // Filter row 1
    ScDBData* pDBData = new ScDBData(u"TRANSPOSE_TEST_DATA"_ustr, srcSheet, 0, 0, 0, 3);
    m_pDoc->SetAnonymousDBData(0, std::unique_ptr<ScDBData>(pDBData));

    pDBData->SetAutoFilter(true);
    ScRange aRange;
    pDBData->GetArea(aRange);
    m_pDoc->ApplyFlagsTab(aRange.aStart.Col(), aRange.aStart.Row(), aRange.aEnd.Col(),
                          aRange.aStart.Row(), aRange.aStart.Tab(), ScMF::Auto);

    //create the query param
    ScQueryParam aParam;
    pDBData->GetQueryParam(aParam);
    ScQueryEntry& rEntry = aParam.GetEntry(0);
    rEntry.bDoQuery = true;
    rEntry.nField = 0;
    rEntry.eOp = SC_NOT_EQUAL;
    rEntry.GetQueryItem().mfVal = 2; // value of row A2 -> filtering row 1
    // add queryParam to database range.
    pDBData->SetQueryParam(aParam);

    // perform the query.
    m_pDoc->Query(srcSheet, aParam, true);

    // Check precondition for test: row 1 is hidden/filtered
    SCROW nRow1, nRow2;
    SCROW nFilteredRow1, nFilteredRow2;
    bool bHidden = m_pDoc->RowHidden(SCROW(1), srcSheet, &nRow1, &nRow2);
    CPPUNIT_ASSERT_MESSAGE("row 1 should be hidden", bHidden);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be hidden", SCROW(1), nRow1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be hidden", SCROW(1), nRow2);
    bool bFiltered = m_pDoc->RowFiltered(SCROW(1), srcSheet, &nFilteredRow1, &nFilteredRow2);
    CPPUNIT_ASSERT_MESSAGE("row 1 should be filtered", bFiltered);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be filtered", SCROW(1), nFilteredRow1);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("row 1 should be filtered", SCROW(1), nFilteredRow2);

    // Copy A1:A4 to clip.
    ScRange aSrcRange(0, 0, srcSheet, 0, 3, srcSheet);
    ScDocument aClipDoc(SCDOCMODE_CLIP);
    copyToClip(m_pDoc, aSrcRange, &aClipDoc);

    // transpose
    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, truefalse);

    ScRange aDestRange(1, 1, destSheet, 3, 1, destSheet); // Paste to B2:D2 on Sheet2.
    ScMarkData aMark(m_pDoc->GetSheetLimits());
    aMark.SetMarkArea(aDestRange);
    m_pDoc->CopyFromClip(aDestRange, aMark, InsertDeleteFlags::ALL, nullptr, pTransClip.get(), true,
                         falsefalse);
    pTransClip.reset();

    // Check pasted content to make sure they reference the correct cells.
    ScFormulaCell* pFC = m_pDoc->GetFormulaCell(ScAddress(1, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell B2.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell B2", u"=$Sheet1.$A$1"_ustr,
                                 m_pDoc->GetFormula(1, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(1.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(2, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell C2", u"=$Sheet1.$A$3"_ustr,
                                 m_pDoc->GetFormula(2, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(0.0, pFC->GetValue());

    pFC = m_pDoc->GetFormulaCell(ScAddress(3, 1, destSheet));
    CPPUNIT_ASSERT_MESSAGE("This should be a formula cell.", pFC);
    CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell D2", u"=$Sheet1.$A$4"_ustr,
                                 m_pDoc->GetFormula(3, 1, destSheet));
    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());

    m_pDoc->DeleteTab(destSheet);
    m_pDoc->DeleteTab(srcSheet);
}

// Compatibility method since normal copy/paste tests do not test undo
void TestCopyPaste::executeCopyPasteSpecial(bool bApplyFilter, bool bIncludedFiltered, bool bAsLink,
                                            bool bTranspose, bool bMultiRangeSelection,
                                            bool bSkipEmpty, bool bCut,
                                            ScClipParam::Direction eDirection, CalcMode eCalcMode,
                                            InsertDeleteFlags aFlags)
{
    std::unique_ptr<ScUndoCut> pUndoCut;
    std::unique_ptr<ScUndoPaste> pUndoPaste;
    executeCopyPasteSpecial(0, 1, bApplyFilter, bIncludedFiltered, bAsLink, bTranspose,
                            bMultiRangeSelection, bSkipEmpty, pUndoCut, pUndoPaste, bCut,
                            eDirection, eCalcMode, aFlags);
}

// This method is used to create the different copy/paste special test cases.
// Principle: Creation of test cases is parameterized, whereas checking uses a minimum of logic
void TestCopyPaste::executeCopyPasteSpecial(const SCTAB srcSheet, const SCTAB destSheet,
                                            bool bApplyFilter, bool bIncludedFiltered, bool bAsLink,
                                            bool bTranspose, bool bMultiRangeSelection,
--> --------------------

--> maximum size reached

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

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

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