[Libreoffice-commits] core.git: sc/inc sc/qa sc/source

scito (via logerrit) logerrit at kemper.freedesktop.org
Thu Apr 22 13:12:06 UTC 2021


 sc/inc/clipparam.hxx                |   10 
 sc/inc/document.hxx                 |   12 
 sc/inc/table.hxx                    |   29 
 sc/qa/unit/ucalc.cxx                | 5361 +++++++++++++++++++++++++++++++++++-
 sc/qa/unit/ucalc.hxx                |  122 
 sc/qa/unit/uicalc/uicalc.cxx        |   45 
 sc/source/core/data/clipparam.cxx   |   50 
 sc/source/core/data/document.cxx    |  124 
 sc/source/core/data/formulacell.cxx |   22 
 sc/source/core/data/table2.cxx      |  258 +
 sc/source/ui/inc/viewfunc.hxx       |    7 
 sc/source/ui/view/viewfun3.cxx      |   30 
 12 files changed, 5919 insertions(+), 151 deletions(-)

New commits:
commit 6491c205acb3c166d93ef6a41199d344e21d98ac
Author:     scito <info at scito.ch>
AuthorDate: Wed Apr 21 07:36:43 2021 +0200
Commit:     Jan Holesovsky <kendy at collabora.com>
CommitDate: Thu Apr 22 15:11:17 2021 +0200

    tdf#107348 tdf#45958 tdf#141215 tdf#141683 fix filtered/transpose paste
    
    Row filtering, Special Paste transposing and multi range selections had
    various issues. Since the same methods are used in different code paths,
    I made a unified fix for these issues.
    Moreover, Special Paste dialog allows the combination of options.
    There are about 50 test cases for these various combinations of filtering,
    selection, transposing and pasting options.
    
    The following cases are supported and checked in test combinations:
    
    * Transposing
    * Filtering
    * Multi range column and row selection
    * All cell types: number, string, formula, rich text, empty
    * Notes
    * Formatting patterns (e.g. cell backgrounds and borders)
    * Empty cell skipping
    * Special Paste as link
    * Merged cells
    * Formula references:
        * Relative and absolute references
        * References to rows before and after filtered row
        * References to filtered row
        * Double references (e.g. A1:A3)
    
    Notably the following cases are fixed and tested:
    
    * Transposing of filtered data (tdf#107348)
    * Copy/Paste of filtered multi range selections (tdf#45958)
    * Transposing of filtered multi range selections (tdf#107348, tdf#45958)
    * Notes at different position (tdf#141215 ^)
    * Transposed multi range selection pasted as link (tdf#141683 ^)
    
    ^ = discovered during tests
    
    Due to the unit tests, I refactored some code. Mainly, I extracted
    methods to improve the readability and to avoid code duplication.
    Change-Id: Iccccd17d3b4deee29ac5fc7c11e17244da0d4054
    
    Change-Id: I43cf630890e0081e55fd04cf2f8e5afdb6d9bebe
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/114450
    Tested-by: Jenkins
    Reviewed-by: Mike Kaganski <mike.kaganski at collabora.com>
    Reviewed-by: Jan Holesovsky <kendy at collabora.com>

diff --git a/sc/inc/clipparam.hxx b/sc/inc/clipparam.hxx
old mode 100644
new mode 100755
index 7a4513ffb502..55f37fe1d89e
--- a/sc/inc/clipparam.hxx
+++ b/sc/inc/clipparam.hxx
@@ -21,6 +21,7 @@
 
 #include "rangelst.hxx"
 #include "charthelper.hxx"
+#include "document.hxx"
 
 /**
  * This struct stores general clipboard parameters associated with a
@@ -52,14 +53,19 @@ struct SC_DLLPUBLIC ScClipParam
      * Same as the above method, but returns the row size of the compressed
      * range.
      */
-    SCROW getPasteRowSize();
+    SCROW getPasteRowSize(const ScDocument& rSrcDoc, bool bIncludeFiltered);
 
     /**
      * Return a single range that encompasses all individual ranges.
      */
     ScRange getWholeRange() const;
 
-    void transpose();
+    /**
+     * Transpose the clip parameters.
+     * Filtered rows are removed from parameters.
+     */
+    void transpose(const ScDocument& rSrcDoc, bool bIncludeFiltered,
+                   bool bIsMultiRangeRowFilteredTranspose);
 
     sal_uInt32 getSourceDocID() const { return mnSourceDocID; }
     void setSourceDocID( sal_uInt32 nVal ) { mnSourceDocID = nVal; }
diff --git a/sc/inc/document.hxx b/sc/inc/document.hxx
old mode 100644
new mode 100755
index 96d7ec940059..f90e74e9c030
--- a/sc/inc/document.hxx
+++ b/sc/inc/document.hxx
@@ -1605,9 +1605,12 @@ public:
     void CopyBlockFromClip( sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1,
                             SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark,
                             SCCOL nDx, SCROW nDy );
-    void CopyNonFilteredFromClip( sc::CopyFromClipContext& rCxt, SCCOL nCol1,
-                                  SCROW nRow1, SCCOL nCol2, SCROW nRow2,
-                                  const ScMarkData& rMark, SCCOL nDx, SCROW & rClipStartRow );
+    /**
+     * @return the number of non-filtered rows.
+     */
+    SCROW CopyNonFilteredFromClip(sc::CopyFromClipContext& rCxt, SCCOL nCol1, SCROW nRow1,
+                                  SCCOL nCol2, SCROW nRow2, const ScMarkData& rMark, SCCOL nDx,
+                                  SCROW& rClipStartRow, SCROW nClipEndRow);
 
     void StartListeningFromClip( SCCOL nCol1, SCROW nRow1,
                                  SCCOL nCol2, SCROW nRow2,
@@ -1642,7 +1645,8 @@ public:
 
     bool                IsClipboardSource() const;
 
-    SC_DLLPUBLIC void   TransposeClip( ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink );
+    SC_DLLPUBLIC void TransposeClip(ScDocument* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
+                                    bool bIncludeFiltered);
 
     ScClipParam&        GetClipParam();
     void                SetClipParam(const ScClipParam& rParam);
diff --git a/sc/inc/table.hxx b/sc/inc/table.hxx
old mode 100644
new mode 100755
index 211e317cb06d..416cbb5d9337
--- a/sc/inc/table.hxx
+++ b/sc/inc/table.hxx
@@ -540,10 +540,15 @@ public:
 
     void        CopyConditionalFormat( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
                             SCCOL nDx, SCROW nDy, const ScTable* pTable);
-    void        TransposeClip( SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2,
-                                ScTable* pTransClip, InsertDeleteFlags nFlags, bool bAsLink );
+    /**
+     * @param nRowDestOffset adjustment of destination row position;
+     * used for transposed multi range selection with row direction, otherwise 0
+     */
+    void TransposeClip(SCCOL nCol1, SCROW nRow1, SCCOL nCol2, SCROW nRow2, SCROW nRowDestOffset,
+                       ScTable* pTransClip, InsertDeleteFlags nFlags, bool bAsLink,
+                       bool bIncludeFiltered);
 
-                // mark of this document
+    // mark of this document
     void MixMarked(
         sc::MixDocContext& rCxt, const ScMarkData& rMark, ScPasteFunc nFunction,
         bool bSkipEmpty, const ScTable* pSrcTab );
@@ -1246,8 +1251,22 @@ private:
 
     SCCOL       FindNextVisibleCol(SCCOL nCol, bool bRight) const;
 
-    // Clipboard transpose for notes
-    void TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1, SCROW nRow2);
+    /**
+     * Transpose clipboard patterns
+     * @param nRowDestOffset adjustment of destination row position;
+     * used for transposed multi range row selections, otherwise 0
+     */
+    void TransposeColPatterns(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1,
+                              SCROW nRow2, bool bIncludeFiltered,
+                              const std::vector<SCROW>& rFilteredRows, SCROW nRowDestOffset);
+
+    /**
+     * Transpose clipboard notes
+     * @param nRowDestOffset adjustment of destination row position;
+     * used for transposed multi range row selections, otherwise 0
+     */
+    void TransposeColNotes(ScTable* pTransClip, SCCOL nCol1, SCCOL nCol, SCROW nRow1, SCROW nRow2,
+                           bool bIncludeFiltered, SCROW nRowDestOffset);
 
     ScColumn* FetchColumn( SCCOL nCol );
     const ScColumn* FetchColumn( SCCOL nCol ) const;
diff --git a/sc/qa/unit/ucalc.cxx b/sc/qa/unit/ucalc.cxx
old mode 100644
new mode 100755
index 52dedc42c070..02988bdd5ff4
--- a/sc/qa/unit/ucalc.cxx
+++ b/sc/qa/unit/ucalc.cxx
@@ -20,6 +20,7 @@
 #include <scdll.hxx>
 #include <formulacell.hxx>
 #include <simpleformulacalc.hxx>
+#include <formulaopt.hxx>
 #include <stringutil.hxx>
 #include <scmatrix.hxx>
 #include <drwlayer.hxx>
@@ -158,6 +159,20 @@ void Test::tearDown()
     BootstrapFixture::tearDown();
 }
 
+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 Test::testCollator()
 {
     CollatorWrapper* p = ScGlobal::GetCollator();
@@ -2050,10 +2065,10 @@ void Test::testCellCopy()
     ScAddress aDest(0,1,0);
     OUString aStr("please copy me");
     m_pDoc->SetString(aSrc, "please copy me");
-    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aSrc), aStr);
+    CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aSrc));
     // copy to self - why not ?
     m_pDoc->CopyCellToDocument(aSrc,aDest,*m_pDoc);
-    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetString(aDest), aStr);
+    CPPUNIT_ASSERT_EQUAL(aStr, m_pDoc->GetString(aDest));
 }
 
 void Test::testSheetCopy()
@@ -3649,7 +3664,6 @@ void Test::testCopyPasteAsLink()
 void Test::testCopyPasteTranspose()
 {
     m_pDoc->InsertTab(0, "Sheet1");
-    m_pDoc->InsertTab(1, "Sheet2");
 
     // We need a drawing layer in order to create caption objects.
     m_pDoc->InitDrawLayer(&getDocShell());
@@ -3677,7 +3691,7 @@ void Test::testCopyPasteTranspose()
     copyToClip(m_pDoc, aSrcRange, &aNewClipDoc);
 
     ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
-    aNewClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, false);
+    aNewClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::ALL, false, false);
 
     ScRange aDestRange(3,1,1,3,3,1);//target: Sheet2.D2:D4
     ScMarkData aMark(m_pDoc->GetSheetLimits());
@@ -3689,9 +3703,9 @@ void Test::testCopyPasteTranspose()
     OUString aString = m_pDoc->GetString(3, 3, 1);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Cell Sheet2.D4 should contain: test", OUString("test"), aString);
     double fValue = m_pDoc->GetValue(ScAddress(3,1,1));
-    ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied cell should return 1", fValue, 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", fValue, 2);
+    ASSERT_DOUBLES_EQUAL_MESSAGE("transposed copied formula should return 2", 2, fValue);
     m_pDoc->GetFormula(3, 2, 1, aString);
     CPPUNIT_ASSERT_EQUAL_MESSAGE("transposed formula should point on Sheet2.D2", OUString("=D2+1"), aString);
 
@@ -3700,15 +3714,5344 @@ void Test::testCopyPasteTranspose()
     CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D3", m_pDoc->HasNote(ScAddress(3, 2, 1)));
     CPPUNIT_ASSERT_MESSAGE("There should be a note on Sheet2.D4", m_pDoc->HasNote(ScAddress(3, 3, 1)));
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D2",
-            m_pDoc->GetNote(ScAddress(3, 1, 1))->GetText(),  m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText());
+                                 m_pDoc->GetNote(ScAddress(0, 0, 0))->GetText(),
+                                 m_pDoc->GetNote(ScAddress(3, 1, 1))->GetText());
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D3",
-            m_pDoc->GetNote(ScAddress(3, 2, 1))->GetText(),  m_pDoc->GetNote(ScAddress(1, 0, 0))->GetText());
+                                 m_pDoc->GetNote(ScAddress(1, 0, 0))->GetText(),
+                                 m_pDoc->GetNote(ScAddress(3, 2, 1))->GetText());
     CPPUNIT_ASSERT_EQUAL_MESSAGE("Content of cell note on Sheet2.D4",
-            m_pDoc->GetNote(ScAddress(3, 3, 1))->GetText(),  m_pDoc->GetNote(ScAddress(2, 0, 0))->GetText());
+                                 m_pDoc->GetNote(ScAddress(2, 0, 0))->GetText(),
+                                 m_pDoc->GetNote(ScAddress(3, 3, 1))->GetText());
 
     m_pDoc->DeleteTab(1);
     m_pDoc->DeleteTab(0);
+}
+
+void Test::testCopyPasteSpecialMergedCellsTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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(srcSheet, 0, 1, 1, 1, false); // Merge A2 and B2
+    m_pDoc->DoMerge(srcSheet, 0, 2, 1, 2, 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, true, false);
+
+    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);
+}
+
+void Test::testCopyPasteSpecialMergedCellsFilteredTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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(srcSheet, 0, 1, 1, 1, false); // Merge A2 and B2
+    m_pDoc->DoMerge(srcSheet, 0, 2, 1, 2, false); // Merge A3 and B3
+
+    // Filter row 1
+    ScDBData* pDBData = new ScDBData("TRANSPOSE_TEST_DATA", 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, true, false);
+
+    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
+void Test::testCopyPasteSpecialAsLinkTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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, true, false);
+
+    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(),
+                         true, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$2"), aString);
+    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);
+    m_pDoc->GetFormula(4, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell E2", OUString("=$Sheet1.$A$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// InsertDeleteFlags::CONTENTS
+void Test::testCopyPasteSpecialAsLinkFilteredTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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("TRANSPOSE_TEST_DATA", 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, true, false);
+
+    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(),
+                         true, false, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(3, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D2", OUString("=$Sheet1.$A$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// tdf#141683
+// InsertDeleteFlags::CONTENTS
+void Test::testCopyPasteSpecialMultiRangeRowAsLinkTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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, false, false);
+
+    // transpose
+    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, true, false);
+
+    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::CONTENTS,
+                                   pTransClip.get(), true, false /* false fixes tdf#141683 */,
+                                   false, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(1, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B3", OUString("=$Sheet1.$B$1"), aString);
+    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);
+    m_pDoc->GetFormula(1, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B5", OUString("=$Sheet1.$D$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$3"), aString);
+    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);
+    m_pDoc->GetFormula(2, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C3", OUString("=$Sheet1.$B$3"), aString);
+    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);
+    m_pDoc->GetFormula(2, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C5", OUString("=$Sheet1.$D$3"), aString);
+    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// tdf#141683
+// InsertDeleteFlags::CONTENTS
+void Test::testCopyPasteSpecialMultiRangeRowAsLinkFilteredTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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("TRANSPOSE_TEST_DATA", 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 neative 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, false, false);
+
+    printRange(m_pDoc, aClipParam.getWholeRange(), "Src range");
+    // transpose
+    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, true, false);
+
+    printRange(&aClipDoc, ScRange(0, 0, 0, 4, 5, 0), "Base doc (&aNewClipDoc)");
+    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::CONTENTS,
+                                   pTransClip.get(), true, false /* false fixes tdf#141683 */,
+                                   false, false);
+    pTransClip.reset();
+    printRange(m_pDoc, aDestRange, "Transposed dest sheet");
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(1, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B3", OUString("=$Sheet1.$B$1"), aString);
+    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);
+    m_pDoc->GetFormula(1, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B5", OUString("=$Sheet1.$D$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$3"), aString);
+    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);
+    m_pDoc->GetFormula(2, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C3", OUString("=$Sheet1.$B$3"), aString);
+    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);
+    m_pDoc->GetFormula(2, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C5", OUString("=$Sheet1.$D$3"), aString);
+    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);
+    m_pDoc->GetFormula(3, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D2", OUString("=$Sheet1.$A$6"), aString);
+    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);
+    m_pDoc->GetFormula(3, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D3", OUString("=$Sheet1.$B$6"), aString);
+    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);
+    m_pDoc->GetFormula(3, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D5", OUString("=$Sheet1.$D$6"), aString);
+    CPPUNIT_ASSERT_EQUAL(114.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// tdf#141683
+// InsertDeleteFlags::CONTENTS
+void Test::testCopyPasteSpecialMultiRangeColAsLinkTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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, false, false);
+
+    // transpose
+    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, true, false);
+
+    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::CONTENTS,
+                                   pTransClip.get(), true, false /* false fixes tdf#141683 */,
+                                   false, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$2"), aString);
+    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);
+    m_pDoc->GetFormula(4, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell E2", OUString("=$Sheet1.$A$4"), aString);
+    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);
+    m_pDoc->GetFormula(1, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B3", OUString("=$Sheet1.$C$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C3", OUString("=$Sheet1.$C$2"), aString);
+    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);
+    m_pDoc->GetFormula(4, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell E3", OUString("=$Sheet1.$C$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// tdf#141683
+// InsertDeleteFlags::CONTENTS
+void Test::testCopyPasteSpecialMultiRangeColAsLinkFilteredTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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("TRANSPOSE_TEST_DATA", 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, false, false);
+
+    // transpose
+    ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+    aClipDoc.TransposeClip(pTransClip.get(), InsertDeleteFlags::CONTENTS, true, false);
+
+    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::CONTENTS,
+                                   pTransClip.get(), true, false /* false fixes tdf#141683 */,
+                                   false, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(3, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D2", OUString("=$Sheet1.$A$4"), aString);
+    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);
+    m_pDoc->GetFormula(1, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B3", OUString("=$Sheet1.$C$1"), aString);
+    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);
+    m_pDoc->GetFormula(3, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D3", OUString("=$Sheet1.$C$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(14.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// InsertDeleteFlags::ALL
+void Test::testCopyPasteSpecialAllAsLinkTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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, true, false);
+
+    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();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$2"), aString);
+    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);
+    m_pDoc->GetFormula(3, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D2", OUString("=$Sheet1.$A$3"), aString);
+    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);
+    m_pDoc->GetFormula(4, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell E2", OUString("=$Sheet1.$A$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// InsertDeleteFlags::ALL
+void Test::testCopyPasteSpecialAllAsLinkFilteredTranspose()
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    sc::AutoCalcSwitch aACSwitch(*m_pDoc, true); // Turn on auto calc.
+
+    m_pDoc->InsertTab(srcSheet, "Sheet1");
+    m_pDoc->InsertTab(destSheet, "Sheet2");
+
+    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("TRANSPOSE_TEST_DATA", 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, true, false);
+
+    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,
+                         false, false);
+    pTransClip.reset();
+
+    OUString aString;
+    // 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);
+    m_pDoc->GetFormula(1, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell B2", OUString("=$Sheet1.$A$1"), aString);
+    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);
+    m_pDoc->GetFormula(2, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell C2", OUString("=$Sheet1.$A$3"), aString);
+    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);
+    m_pDoc->GetFormula(3, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL_MESSAGE("Formula cell D2", OUString("=$Sheet1.$A$4"), aString);
+    CPPUNIT_ASSERT_EQUAL(4.0, pFC->GetValue());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+// 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 Test::executeCopyPasteSpecial(bool bApplyFilter, bool bIncludedFiltered, bool bAsLink,
+                                   bool bTranspose, bool bMultiRangeSelection, bool bSkipEmpty,
+                                   ScClipParam::Direction eDirection = ScClipParam::Column,
+                                   bool bCalcAll = false,
+                                   InsertDeleteFlags aFlags
+                                   = InsertDeleteFlags::CONTENTS | InsertDeleteFlags::ATTRIB)
+{
+    // Reset settings needed for SUMIF() if run with other tests
+    ScFormulaOptions aOptions;
+    aOptions.SetFormulaSepArg(";");
+    getDocShell().SetFormulaOptions(aOptions);
+
+    const SCTAB srcSheet = 0;
+    m_pDoc->InsertTab(srcSheet, "SrcSheet");
+
+    // We need a drawing layer in order to create caption objects.
+    m_pDoc->InitDrawLayer(&getDocShell());
+    ScFieldEditEngine& rEditEngine = m_pDoc->GetEditEngine();
+
+    /*
+         | A   |    B     | C    |  D  |     E      |        F            |
+
+    1r   | 1 B*| =A1+10  *| a    | R1 *| =A1+A3+60  | =SUMIF(A1:A4;"<4")  |
+    2r   | 2 B*| =A2+20 b | b   *| R2 *|            |                    *| <- filtered row
+    3r   | 3 B*| =D3+30 b*| c   *|  5 *|          B*|                     |
+    4    | 4   | =A2+40 b*| d   *| R4 *| =A1+A3+70 *|    =B$1+$A$3+80    *|
+   (5r   | 6   |    q     | r bB*| s bB|     t      |          u          |) optional, for row range
+   (6    | -1  |    -2    |  -3  | -4  |     -5     |          -6         |) optional, for row range
+   (7r   | -11 |    -12   | -13  | -14 |     -15    |          -16        |) optional, for row range
+   (8    | -21 |    -22   | -23  | -24 |     -25    |          -26        |) optional, for row range
+
+          \______________/      \________________________________________/
+             col range 1                     col range 2
+
+    * means note attached
+    B means background
+    b means border
+    r means row selected for row range in multi range selection
+
+    The following test scenarios can be created:
+
+    * Filtered row
+    * Transpose
+    * All cell types: numbers, strings, formulas, rich text, empty cell
+    * Notes at different position
+    * Formula references to rows before and after filtered row
+    * Double reference (e.g. A1:A3)
+    * Relative and absolute references
+        * absolute references are not changed by transposing
+    * Formatting patterns (e.g. cell backgrounds and borders)
+    * Multi range selection with direction column and row
+
+    */
+    SCCOL nSrcCols = 6;
+    SCROW nSrcRows = 4;
+    // Add additional row for MultiRange test cases
+    if (bMultiRangeSelection)
+    {
+        nSrcRows = eDirection == ScClipParam::Row ? nSrcRows + 2 : nSrcRows;
+        nSrcCols = eDirection == ScClipParam::Column ? nSrcCols + 1 : nSrcCols;
+    }
+
+    // col A
+    m_pDoc->SetValue(0, 0, srcSheet, 1);
+    m_pDoc->SetValue(0, 1, srcSheet, 2);
+    m_pDoc->SetValue(0, 2, srcSheet, 3);
+    m_pDoc->SetValue(0, 3, srcSheet, 4);
+    // col B
+    m_pDoc->SetString(1, 0, srcSheet, "=A1+10");
+    m_pDoc->SetString(1, 1, srcSheet, "=A2+20");
+    m_pDoc->SetString(1, 2, srcSheet, "=D3+30");
+    m_pDoc->SetString(1, 3, srcSheet, "=A2+40");
+    // col C
+    m_pDoc->SetString(2, 0, srcSheet, "a");
+    m_pDoc->SetString(2, 1, srcSheet, "b");
+    m_pDoc->SetString(2, 2, srcSheet, "c");
+    m_pDoc->SetString(2, 3, srcSheet, "d");
+    // col D
+    rEditEngine.SetTextCurrentDefaults("R1");
+    m_pDoc->SetEditText(ScAddress(3, 0, srcSheet), rEditEngine.CreateTextObject());
+    rEditEngine.SetTextCurrentDefaults("R2");
+    m_pDoc->SetEditText(ScAddress(3, 1, srcSheet), rEditEngine.CreateTextObject());
+    m_pDoc->SetValue(3, 2, srcSheet, 5);
+    rEditEngine.SetTextCurrentDefaults("R4");
+    m_pDoc->SetEditText(ScAddress(3, 3, srcSheet), rEditEngine.CreateTextObject());
+    // col E
+    m_pDoc->SetValue(4, 0, srcSheet, 9);
+    m_pDoc->SetString(4, 0, srcSheet, "=A1+A3+60");
+    m_pDoc->SetEmptyCell(ScAddress(4, 1, srcSheet));
+    m_pDoc->SetEmptyCell(ScAddress(4, 2, srcSheet));
+    m_pDoc->SetString(4, 3, srcSheet, "=A1+A3+70");
+    // col F
+    m_pDoc->SetValue(5, 0, srcSheet, 9);
+    m_pDoc->SetString(5, 0, srcSheet, "=SUMIF(A1:A4;\"<4\")");
+    m_pDoc->SetEmptyCell(ScAddress(5, 1, srcSheet));
+    m_pDoc->SetEmptyCell(ScAddress(5, 2, srcSheet));
+    m_pDoc->SetString(5, 3, srcSheet, "=B$1+$A$3+80");
+
+    const SfxPoolItem* pItem = nullptr;
+
+    // row 4, additional row for MultiRange test case, otherwise not selected
+    m_pDoc->SetValue(0, 4, srcSheet, 6);
+    m_pDoc->SetString(1, 4, srcSheet, "q");
+    m_pDoc->SetString(2, 4, srcSheet, "r");
+    m_pDoc->SetString(3, 4, srcSheet, "s");
+    m_pDoc->SetString(4, 4, srcSheet, "t");
+    m_pDoc->SetString(5, 4, srcSheet, "u");
+
+    // row 5, not selected
+    m_pDoc->SetValue(0, 5, srcSheet, -1);
+    m_pDoc->SetValue(1, 5, srcSheet, -2);
+    m_pDoc->SetValue(2, 5, srcSheet, -3);
+    m_pDoc->SetValue(3, 5, srcSheet, -4);
+    m_pDoc->SetValue(4, 5, srcSheet, -5);
+    m_pDoc->SetValue(5, 5, srcSheet, -6);
+
+    // row 6, additional row for MultiRange test case, otherwise not selected
+    m_pDoc->SetValue(0, 6, srcSheet, -11);
+    m_pDoc->SetValue(1, 6, srcSheet, -12);
+    m_pDoc->SetValue(2, 6, srcSheet, -13);
+    m_pDoc->SetValue(3, 6, srcSheet, -14);
+    m_pDoc->SetValue(4, 6, srcSheet, -15);
+    m_pDoc->SetValue(5, 6, srcSheet, -16);
+
+    // row 7, additional row for MultiRange test case, otherwise not selected
+    m_pDoc->SetValue(0, 7, srcSheet, -21);
+    m_pDoc->SetValue(1, 7, srcSheet, -22);
+    m_pDoc->SetValue(2, 7, srcSheet, -23);
+    m_pDoc->SetValue(3, 7, srcSheet, -24);
+    m_pDoc->SetValue(4, 7, srcSheet, -25);
+    m_pDoc->SetValue(5, 7, srcSheet, -26);
+
+    // Col G, not selected
+    m_pDoc->SetValue(6, 0, srcSheet, 111);
+    m_pDoc->SetValue(6, 1, srcSheet, 112);
+    m_pDoc->SetValue(6, 2, srcSheet, 113);
+    m_pDoc->SetValue(6, 3, srcSheet, 114);
+    m_pDoc->SetValue(6, 4, srcSheet, 115);
+    m_pDoc->SetValue(6, 5, srcSheet, 116);
+
+    // Col H, additional col for MultiRange test case, otherwise not selected
+    m_pDoc->SetValue(7, 0, srcSheet, 121);
+    m_pDoc->SetValue(7, 1, srcSheet, 122);
+    m_pDoc->SetValue(7, 2, srcSheet, 123);
+    m_pDoc->SetValue(7, 3, srcSheet, 124);
+    m_pDoc->SetValue(7, 4, srcSheet, 125);
+    m_pDoc->SetValue(7, 5, srcSheet, 126);
+
+    // Col J, not selected
+    m_pDoc->SetValue(8, 0, srcSheet, 131);
+    m_pDoc->SetValue(8, 1, srcSheet, 132);
+    m_pDoc->SetValue(8, 2, srcSheet, 133);
+    m_pDoc->SetValue(8, 3, srcSheet, 134);
+    m_pDoc->SetValue(8, 4, srcSheet, 135);
+    m_pDoc->SetValue(8, 5, srcSheet, 136);
+
+    // add patterns
+    ScPatternAttr aCellBlueColor(m_pDoc->GetPool());
+    aCellBlueColor.GetItemSet().Put(SvxBrushItem(COL_BLUE, ATTR_BACKGROUND));
+    m_pDoc->ApplyPatternAreaTab(0, 0, 0, 2, srcSheet, aCellBlueColor);
+
+    // Check pattern precondition
+    m_pDoc->GetPattern(ScAddress(0, 0, srcSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.A1 has a pattern", pItem);
+    CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+    m_pDoc->GetPattern(ScAddress(0, 1, srcSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.A2 has a pattern", pItem);
+    CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+    m_pDoc->GetPattern(ScAddress(0, 3, srcSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.A4 has no pattern", !pItem);
+
+    // row 2 on empty cell
+    ScPatternAttr aCellGreenColor(m_pDoc->GetPool());
+    aCellGreenColor.GetItemSet().Put(SvxBrushItem(COL_GREEN, ATTR_BACKGROUND));
+    m_pDoc->ApplyPatternAreaTab(4, 2, 4, 2, srcSheet, aCellGreenColor);
+
+    // row 4 for multi range row selection
+    ScPatternAttr aCellRedColor(m_pDoc->GetPool());
+    aCellRedColor.GetItemSet().Put(SvxBrushItem(COL_RED, ATTR_BACKGROUND));
+    m_pDoc->ApplyPatternAreaTab(2, 4, 3, 4, srcSheet, aCellRedColor);
+
+    // add borders
+    ::editeng::SvxBorderLine aLine(nullptr, 50, SvxBorderLineStyle::SOLID);
+    SvxBoxItem aBorderItem(ATTR_BORDER);
+    aBorderItem.SetLine(&aLine, SvxBoxItemLine::LEFT);
+    aBorderItem.SetLine(&aLine, SvxBoxItemLine::RIGHT);
+    m_pDoc->ApplyAttr(1, 1, srcSheet, aBorderItem);
+    m_pDoc->ApplyAttr(1, 2, srcSheet, aBorderItem);
+    m_pDoc->ApplyAttr(1, 3, srcSheet, aBorderItem);
+    // Check border precondition
+    pItem = m_pDoc->GetAttr(ScAddress(1, 0, srcSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.B1 has a border", pItem);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    pItem = m_pDoc->GetAttr(ScAddress(1, 1, srcSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.B2 has a border", pItem);
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    // Check border precondition 2
+    m_pDoc->GetPattern(ScAddress(1, 1, srcSheet))->GetItemSet().HasItem(ATTR_BORDER, &pItem);
+    CPPUNIT_ASSERT_MESSAGE("SrcSheet.B2 has a border", pItem);
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    // row 4 for multi range row selection
+    ::editeng::SvxBorderLine aDoubleLine(nullptr, 50, SvxBorderLineStyle::DOUBLE);
+    SvxBoxItem aDoubleBorderItem(ATTR_BORDER);
+    aDoubleBorderItem.SetLine(&aDoubleLine, SvxBoxItemLine::TOP);
+    aDoubleBorderItem.SetLine(&aDoubleLine, SvxBoxItemLine::BOTTOM);
+    m_pDoc->ApplyAttr(2, 4, srcSheet, aDoubleBorderItem);
+    m_pDoc->ApplyAttr(3, 4, srcSheet, aDoubleBorderItem);
+
+    // add notes to A1:F4
+
+    // add notes row 0
+    ScAddress aAdrA1(0, 0, srcSheet);
+    ScPostIt* pNoteA1 = m_pDoc->GetOrCreateNote(aAdrA1);
+    pNoteA1->SetText(aAdrA1, "Hello world in A1");
+    ScAddress aAdrB1(1, 0, srcSheet);
+    ScPostIt* pNoteB1 = m_pDoc->GetOrCreateNote(aAdrB1);
+    pNoteB1->SetText(aAdrB1, "Hello world in B1");
+    // No note on C1
+    ScAddress aAdrD1(3, 0, srcSheet);
+    ScPostIt* pNoteD1 = m_pDoc->GetOrCreateNote(aAdrD1);
+    pNoteD1->SetText(aAdrD1, "Hello world in D1");
+    // No note on E1
+    // No note on F1
+
+    // add notes row 1
+    ScAddress aAdrA2(0, 1, srcSheet);
+    ScPostIt* pNoteA2 = m_pDoc->GetOrCreateNote(aAdrA2);
+    pNoteA2->SetText(aAdrA2, "Hello world in A2");
+    // No note on B2
+    ScAddress aAdrC2(2, 1, srcSheet);
+    ScPostIt* pNoteC2 = m_pDoc->GetOrCreateNote(aAdrC2);
+    pNoteC2->SetText(aAdrC2, "Hello world in C2");
+    ScAddress aAdrD2(3, 1, srcSheet);
+    ScPostIt* pNoteD2 = m_pDoc->GetOrCreateNote(aAdrD2);
+    pNoteD2->SetText(aAdrD2, "Hello world in D2");
+    ScAddress aAdrE2(4, 2, srcSheet);
+    ScPostIt* pNoteE2 = m_pDoc->GetOrCreateNote(aAdrE2);
+    pNoteE2->SetText(aAdrE2, "Hello world in E2");
+    ScAddress aAdrF2(5, 1, srcSheet);
+    ScPostIt* pNoteF2 = m_pDoc->GetOrCreateNote(aAdrF2);
+    pNoteF2->SetText(aAdrF2, "Hello world in F2");
+
+    // add notes row 2
+    ScAddress aAdrA3(0, 2, srcSheet);
+    ScPostIt* pNoteA3 = m_pDoc->GetOrCreateNote(aAdrA3);
+    pNoteA3->SetText(aAdrA3, "Hello world in A3");
+    ScAddress aAdrB3(1, 2, srcSheet);
+    ScPostIt* pNoteB3 = m_pDoc->GetOrCreateNote(aAdrB3);
+    pNoteB3->SetText(aAdrB3, "Hello world in B3");
+    ScAddress aAdrC3(2, 2, srcSheet);
+    ScPostIt* pNoteC3 = m_pDoc->GetOrCreateNote(aAdrC3);
+    pNoteC3->SetText(aAdrC3, "Hello world in C3");
+    ScAddress aAdrD3(3, 2, srcSheet);
+    ScPostIt* pNoteD3 = m_pDoc->GetOrCreateNote(aAdrD3);
+    pNoteD3->SetText(aAdrD3, "Hello world in D3");
+    // No note on E3
+    // No note on F3
+
+    // add notes row 3
+    // No note on A4
+    ScAddress aAdrB4(1, 3, srcSheet);
+    ScPostIt* pNoteB4 = m_pDoc->GetOrCreateNote(aAdrB4);
+    pNoteB4->SetText(aAdrB4, "Hello world in B4");
+    ScAddress aAdrC4(2, 3, srcSheet);
+    ScPostIt* pNoteC4 = m_pDoc->GetOrCreateNote(aAdrC4);
+    pNoteC4->SetText(aAdrC4, "Hello world in C4");
+    ScAddress aAdrD4(3, 3, srcSheet);
+    ScPostIt* pNoteD4 = m_pDoc->GetOrCreateNote(aAdrD4);
+    pNoteD4->SetText(aAdrD4, "Hello world in D4");
+    ScAddress aAdrE4(4, 3, srcSheet);
+    ScPostIt* pNoteE4 = m_pDoc->GetOrCreateNote(aAdrE4);
+    pNoteE4->SetText(aAdrE4, "Hello world in E4");
+    ScAddress aAdrF4(5, 3, srcSheet);
+    ScPostIt* pNoteF4 = m_pDoc->GetOrCreateNote(aAdrF4);
+    pNoteF4->SetText(aAdrF4, "Hello world in F4");
+
+    // row 4 for multi range row selection
+    ScAddress aAdrC5(2, 4, srcSheet);
+    ScPostIt* pNoteC5 = m_pDoc->GetOrCreateNote(aAdrC5);
+    pNoteC5->SetText(aAdrC5, "Hello world in C5");
+
+    // Filter out row 1
+    if (bApplyFilter)
+    {
+        ScDBData* pDBData
+            = new ScDBData("TRANSPOSE_TEST_DATA", srcSheet, 0, 0, nSrcCols - 1, nSrcRows - 1);
+        m_pDoc->SetAnonymousDBData(srcSheet, 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);
+    }
+
+    // create destination sheet
+    const SCTAB destSheet = 1;
+    m_pDoc->InsertTab(destSheet, "DestSheet");
+    // set cells to 1000 to check empty cell behaviour and to detect destination range problems
+    for (int i = 0; i < 10; ++i)
+        for (int j = 0; j < 10; ++j)
+            m_pDoc->SetValue(i, j, destSheet, 1000);
+
+    // transpose clipboard, paste on DestSheet
+    ScDocument aNewClipDoc(SCDOCMODE_CLIP);
+    ScMarkData aDestMark(m_pDoc->GetSheetLimits());
+    if (!bMultiRangeSelection)
+    {
+        ScRange aSrcRange(0, 0, srcSheet, nSrcCols - 1, nSrcRows - 1, srcSheet);
+        copyToClip(m_pDoc, aSrcRange, &aNewClipDoc);
+
+        // ScDocument::TransposeClip() and ScDocument::CopyFromClip() calls
+        // analog to ScViewFunc::PasteFromClip()
+        if (bTranspose)
+        {
+            ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+            aNewClipDoc.TransposeClip(pTransClip.get(), aFlags, bAsLink, bIncludedFiltered);
+            ScRange aDestRange(3, 1, destSheet, 3 + nSrcRows - 1, 1 + nSrcCols - 1,
+                               destSheet); //target: D2:F6
+            aDestMark.SetMarkArea(aDestRange);
+            m_pDoc->CopyFromClip(aDestRange, aDestMark, aFlags, nullptr, pTransClip.get(), true,
+                                 bAsLink, bIncludedFiltered, bSkipEmpty);
+            pTransClip.reset();
+        }
+        else
+        {
+            ScRange aDestRange(3, 1, destSheet, 3 + nSrcCols - 1, 1 + nSrcRows - 1,
+                               destSheet); //target: D2:I5
+            aDestMark.SetMarkArea(aDestRange);
+            m_pDoc->CopyFromClip(aDestRange, aDestMark, aFlags, nullptr, &aNewClipDoc, true,
+                                 bAsLink, bIncludedFiltered, bSkipEmpty);
+        }
+    }
+    else
+    {
+        ScMarkData aSrcMark(m_pDoc->GetSheetLimits());
+        aSrcMark.SelectOneTable(0);
+        ScClipParam aClipParam;
+        aClipParam.meDirection = eDirection;
+        if (eDirection == ScClipParam::Column)
+        {
+            aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 1, 3, srcSheet)); // A1:B4
+            aClipParam.maRanges.push_back(ScRange(3, 0, srcSheet, 5, 3, srcSheet)); // D1:F4
+            aClipParam.maRanges.push_back(ScRange(7, 0, srcSheet, 7, 3, srcSheet)); // H1:H4
+        }
+        else if (eDirection == ScClipParam::Row)
+        {
+            aClipParam.maRanges.push_back(ScRange(0, 0, srcSheet, 5, 2, srcSheet)); // A1:F3
+            aClipParam.maRanges.push_back(ScRange(0, 4, srcSheet, 5, 4, srcSheet)); // A5:F5
+            aClipParam.maRanges.push_back(ScRange(0, 6, srcSheet, 5, 6, srcSheet)); // A7:F7
+        }
+        CPPUNIT_ASSERT(aClipParam.isMultiRange());
+        m_pDoc->CopyToClip(aClipParam, &aNewClipDoc, &aSrcMark, false, false);
+
+        // ScDocument::TransposeClip() and ScDocument::CopyMultiRangeFromClip() calls
+        // analog to ScViewFunc::PasteFromClipToMultiRanges()
+        if (bTranspose)
+        {
+            printRange(m_pDoc, aClipParam.getWholeRange(), "Src range");
+            ScDocumentUniquePtr pTransClip(new ScDocument(SCDOCMODE_CLIP));
+            aNewClipDoc.TransposeClip(pTransClip.get(), aFlags, bAsLink, bIncludedFiltered);
+            ScRange aDestRange(3, 1, destSheet, 3 + nSrcRows - 1, 1 + nSrcCols - 1 - 1,
+                               destSheet); //target col: D2:G6, target row: D2:H6
+            aDestMark.SetMarkArea(aDestRange);
+            printRange(&aNewClipDoc, ScRange(0, 0, 0, nSrcCols, nSrcRows, 0),
+                       "Base doc (&aNewClipDoc)");
+            printRange(pTransClip.get(), ScRange(0, 0, 0, nSrcCols, nSrcRows, 0),
+                       "Transposed filtered clipdoc (pTransClip.get())");
+            m_pDoc->CopyMultiRangeFromClip(ScAddress(3, 1, destSheet), aDestMark, aFlags,
+                                           pTransClip.get(), true, bAsLink && !bTranspose,
+                                           bIncludedFiltered, bSkipEmpty);
+            pTransClip.reset();
+            printRange(m_pDoc, aDestRange, "Transposed dest sheet");
+        }
+        else
+        {
+            ScRange aDestRange(3, 1, destSheet, 3 + nSrcCols - 1 - 1, 1 + nSrcRows - 1,
+                               destSheet); //target col: D2:I5, target row: D2:I6
+            aDestMark.SetMarkArea(aDestRange);
+            m_pDoc->CopyMultiRangeFromClip(ScAddress(3, 1, destSheet), aDestMark, aFlags,
+                                           &aNewClipDoc, true, bAsLink && !bTranspose,
+                                           bIncludedFiltered, bSkipEmpty);
+        }
+    }
+    if (bCalcAll)
+        m_pDoc->CalcAll();
+}
+
+void Test::testCopyPasteSpecial()
+{
+    executeCopyPasteSpecial(false, false, false, false, false, false);
+    checkCopyPasteSpecial(false);
+}
+
+void Test::testCopyPasteSpecialFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, false, false);
+    checkCopyPasteSpecialFiltered(false);
+}
+
+void Test::testCopyPasteSpecialIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, false, false);
+    checkCopyPasteSpecial(false);
+}
+
+void Test::testCopyPasteSpecialFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, false, false);
+    checkCopyPasteSpecial(false);
+}
+
+// similar to Test::testCopyPasteTranspose(), but this test is more complex
+void Test::testCopyPasteSpecialTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, false, false);
+    checkCopyPasteSpecialTranspose(false);
+}
+
+// tdf#107348
+void Test::testCopyPasteSpecialFilteredTranspose()
+{
+    executeCopyPasteSpecial(true, false, false, true, false, false);
+    checkCopyPasteSpecialFilteredTranspose(false);
+}
+
+// tdf#107348
+void Test::testCopyPasteSpecialTransposeIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, false, false);
+    checkCopyPasteSpecialTranspose(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeCol()
+{
+    executeCopyPasteSpecial(false, false, false, false, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeColIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(false);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialMultiRangeColFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColFiltered(false);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialMultiRangeColFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeColTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColTranspose(false);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialMultiRangeColFilteredTranspose()
+{
+    executeCopyPasteSpecial(true, false, false, true, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColFilteredTranspose(false);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialMultiRangeColFilteredIncludeFilteredTranspose()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, true, false, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColTranspose(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeRow()
+{
+    executeCopyPasteSpecial(false, false, false, false, true, false, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeRowIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, true, false, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(false);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialMultiRangeRowFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, true, false, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRowFiltered(false);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialMultiRangeRowFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, true, false, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(false);
+}
+
+void Test::testCopyPasteSpecialMultiRangeRowTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, true, false, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowTranspose(false);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialMultiRangeRowFilteredTranspose()
+{
+    executeCopyPasteSpecial(true, false, false, true, true, false, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowFilteredTranspose(false);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialMultiRangeRowFilteredIncludeFilteredTranspose()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, true, false, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowTranspose(false);
+}
+
+void Test::testCopyPasteSpecialSkipEmpty()
+{
+    executeCopyPasteSpecial(false, false, false, false, false, true);
+    checkCopyPasteSpecial(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, false, true);
+    checkCopyPasteSpecialFiltered(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, false, true);
+    checkCopyPasteSpecial(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, false, true);
+    checkCopyPasteSpecial(true);
+}
+
+// similar to Test::testCopyPasteTranspose(), but this test is more complex
+void Test::testCopyPasteSpecialSkipEmptyTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, false, true);
+    checkCopyPasteSpecialTranspose(true);
+}
+
+// tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyFilteredTranspose()
+{
+    executeCopyPasteSpecial(true, false, false, true, false, true);
+    checkCopyPasteSpecialFilteredTranspose(true);
+}
+
+// tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyTransposeIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, false, true);
+    checkCopyPasteSpecialTranspose(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeCol()
+{
+    executeCopyPasteSpecial(false, false, false, false, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(true);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColFiltered(true);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeCol(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColTranspose(true);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColFilteredTranspose()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, false, false, true, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColFilteredTranspose(true);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeColFilteredIncludeFilteredTranspose()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, true, true, ScClipParam::Column);
+    checkCopyPasteSpecialMultiRangeColTranspose(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRow()
+{
+    executeCopyPasteSpecial(false, false, false, false, true, true, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(false, true, false, false, true, true, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(true);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowFiltered()
+{
+    executeCopyPasteSpecial(true, false, false, false, true, true, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRowFiltered(true);
+}
+
+// tdf#45958
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowFilteredIncludeFiltered()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, false, true, true, ScClipParam::Row);
+    checkCopyPasteSpecialMultiRangeRow(true);
+}
+
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowTranspose()
+{
+    executeCopyPasteSpecial(false, false, false, true, true, true, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowTranspose(true);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowFilteredTranspose()
+{
+    executeCopyPasteSpecial(true, false, false, true, true, true, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowFilteredTranspose(true);
+}
+
+// tdf#45958, tdf#107348
+void Test::testCopyPasteSpecialSkipEmptyMultiRangeRowFilteredIncludeFilteredTranspose()
+{
+    // For bIncludeFiltered=true, the non-filtered outcome is expected
+    executeCopyPasteSpecial(true, true, false, true, true, true, ScClipParam::Row, true);
+    checkCopyPasteSpecialMultiRangeRowTranspose(true);
+}
+
+// Base check, nothing filtered, nothing transposed
+void Test::checkCopyPasteSpecial(bool bSkipEmpty)
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    /*
+         |  D  |    E     | F  |  G  |     H      |        I            |
+
+    2    | 1 B*| =D2+10  *| a  | R1 *| =D2+D4+60  | =SUMIF(D2:D5;"<4")  |
+    3    | 2 B*| =D3+20 b | b *| R2 *|            |                    *|
+    4    | 3 B*| =G4+30 b*| c *|  5 *|          B*|                     |
+    5    | 4   | =D3+40 b*| d *| R4 *| =D2+D4+70 *|    =E$1+$A$3+80    *|
+
+    * means note attached
+    B means background
+    b means border
+    */
+
+    OUString aString;
+    double fValue;
+    const EditTextObject* pEditObj;
+    // col 2
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 1, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 3, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 4, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 5, destSheet));
+    // col 3, numbers
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(3, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(1, m_pDoc->GetValue(3, 1, destSheet));
+    ASSERT_DOUBLES_EQUAL(2, m_pDoc->GetValue(3, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(3, m_pDoc->GetValue(3, 3, destSheet));
+    ASSERT_DOUBLES_EQUAL(4, m_pDoc->GetValue(3, 4, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(3, 5, destSheet));
+    // col 4, formulas
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(4, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(11, m_pDoc->GetValue(4, 1, destSheet));
+    m_pDoc->GetFormula(4, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=D2+10"), aString);
+    m_pDoc->GetFormula(4, 2, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=D3+20"), aString);
+    ASSERT_DOUBLES_EQUAL(22, m_pDoc->GetValue(4, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(35, m_pDoc->GetValue(4, 3, destSheet));
+    m_pDoc->GetFormula(4, 3, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=G4+30"), aString);
+    ASSERT_DOUBLES_EQUAL(42, m_pDoc->GetValue(4, 4, destSheet));
+    m_pDoc->GetFormula(4, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=D3+40"), aString);
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(4, 5, destSheet));
+    // col 5, strings
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(5, 0, destSheet));
+    aString = m_pDoc->GetString(5, 0, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    aString = m_pDoc->GetString(5, 1, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("a"), aString);
+    aString = m_pDoc->GetString(5, 2, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("b"), aString);
+    aString = m_pDoc->GetString(5, 3, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("c"), aString);
+    aString = m_pDoc->GetString(5, 4, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("d"), aString);
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(5, 5, destSheet));
+    aString = m_pDoc->GetString(5, 5, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    // col 6, rich text
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(6, 0, destSheet));
+    pEditObj = m_pDoc->GetEditText(ScAddress(6, 0, destSheet));
+    CPPUNIT_ASSERT(pEditObj == nullptr);
+    pEditObj = m_pDoc->GetEditText(ScAddress(6, 1, destSheet));
+    CPPUNIT_ASSERT(pEditObj);
+    CPPUNIT_ASSERT_EQUAL(OUString("R1"), pEditObj->GetText(0));
+    pEditObj = m_pDoc->GetEditText(ScAddress(6, 2, destSheet));
+    CPPUNIT_ASSERT(pEditObj);
+    CPPUNIT_ASSERT_EQUAL(OUString("R2"), pEditObj->GetText(0));
+    ASSERT_DOUBLES_EQUAL(5, m_pDoc->GetValue(6, 3, destSheet));
+    pEditObj = m_pDoc->GetEditText(ScAddress(6, 4, destSheet));
+    CPPUNIT_ASSERT(pEditObj);
+    CPPUNIT_ASSERT_EQUAL(OUString("R4"), pEditObj->GetText(0));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(6, 5, destSheet));
+    pEditObj = m_pDoc->GetEditText(ScAddress(6, 5, destSheet));
+    CPPUNIT_ASSERT(pEditObj == nullptr);
+    // col 7, formulas
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(7, 0, destSheet));
+    aString = m_pDoc->GetString(7, 0, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    m_pDoc->GetFormula(7, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=D2+D4+60"), aString);
+    ASSERT_DOUBLES_EQUAL(64, m_pDoc->GetValue(7, 1, destSheet));
+    if (!bSkipEmpty)
+    {
+        aString = m_pDoc->GetString(7, 2, destSheet);
+        CPPUNIT_ASSERT_EQUAL(EMPTY_OUSTRING, aString);
+        aString = m_pDoc->GetString(7, 3, destSheet);
+        CPPUNIT_ASSERT_EQUAL(EMPTY_OUSTRING, aString);
+    }
+    else
+    {
+        ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(7, 2, destSheet));
+        ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(7, 3, destSheet));
+    }
+    fValue = m_pDoc->GetValue(7, 4, destSheet);
+    m_pDoc->GetFormula(7, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=D2+D4+70"), aString);
+    ASSERT_DOUBLES_EQUAL(74, fValue);
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(7, 5, destSheet));
+    aString = m_pDoc->GetString(7, 5, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    // col 8, formulas
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(8, 0, destSheet));
+    aString = m_pDoc->GetString(8, 0, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    m_pDoc->GetFormula(8, 1, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=SUMIF(D2:D5;\"<4\")"), aString);
+    ASSERT_DOUBLES_EQUAL(6, m_pDoc->GetValue(8, 1, destSheet));
+    if (!bSkipEmpty)
+    {
+        aString = m_pDoc->GetString(8, 2, destSheet);
+        CPPUNIT_ASSERT_EQUAL(EMPTY_OUSTRING, aString);
+        aString = m_pDoc->GetString(8, 3, destSheet);
+        CPPUNIT_ASSERT_EQUAL(EMPTY_OUSTRING, aString);
+    }
+    else
+    {
+        ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(8, 2, destSheet));
+        ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(8, 3, destSheet));
+    }
+    fValue = m_pDoc->GetValue(8, 4, destSheet);
+    m_pDoc->GetFormula(8, 4, destSheet, aString);
+    CPPUNIT_ASSERT_EQUAL(OUString("=E$1+$A$3+80"), aString);
+    ASSERT_DOUBLES_EQUAL(2080, fValue);
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(8, 5, destSheet));
+    aString = m_pDoc->GetString(8, 5, destSheet);
+    CPPUNIT_ASSERT_EQUAL(OUString("1000"), aString);
+    // col 9, numbers
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 1, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 3, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 4, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(9, 5, destSheet));
+
+    // check patterns
+    const SfxPoolItem* pItem = nullptr;
+    m_pDoc->GetPattern(ScAddress(3, 1, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+    m_pDoc->GetPattern(ScAddress(3, 2, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+    m_pDoc->GetPattern(ScAddress(3, 3, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT_EQUAL(COL_BLUE, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+    m_pDoc->GetPattern(ScAddress(3, 4, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT(!pItem);
+    m_pDoc->GetPattern(ScAddress(3, 5, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT(!pItem);
+    m_pDoc->GetPattern(ScAddress(7, 3, destSheet))->GetItemSet().HasItem(ATTR_BACKGROUND, &pItem);
+    CPPUNIT_ASSERT_EQUAL(bSkipEmpty, pItem == nullptr);
+    if (!bSkipEmpty)
+        CPPUNIT_ASSERT_EQUAL(COL_GREEN, static_cast<const SvxBrushItem*>(pItem)->GetColor());
+
+    // check border, left and right borders were transformed to top and bottom borders
+    pItem = m_pDoc->GetAttr(ScAddress(4, 1, destSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    pItem = m_pDoc->GetAttr(ScAddress(4, 2, destSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    pItem = m_pDoc->GetAttr(ScAddress(4, 3, destSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    pItem = m_pDoc->GetAttr(ScAddress(4, 4, destSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(static_cast<const SvxBoxItem*>(pItem)->GetRight());
+    pItem = m_pDoc->GetAttr(ScAddress(4, 5, destSheet), ATTR_BORDER);
+    CPPUNIT_ASSERT(pItem);
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetTop());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetBottom());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetLeft());
+    CPPUNIT_ASSERT(!static_cast<const SvxBoxItem*>(pItem)->GetRight());
+
+    // check notes after transposed copy/paste
+    // check presence of notes
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(3, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(4, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(5, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(6, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(7, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(8, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 0, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 1, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(3, 1, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(4, 1, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(5, 1, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(6, 1, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(7, 1, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(8, 1, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 1, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 2, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(3, 2, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(4, 2, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(5, 2, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(6, 2, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(7, 2, destSheet)));
+    CPPUNIT_ASSERT_EQUAL(!bSkipEmpty, m_pDoc->HasNote(ScAddress(8, 2, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 2, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 3, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(3, 3, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(4, 3, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(5, 3, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(6, 3, destSheet)));
+    CPPUNIT_ASSERT_EQUAL(!bSkipEmpty, m_pDoc->HasNote(ScAddress(7, 3, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(8, 3, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 3, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 4, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(3, 4, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(4, 4, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(5, 4, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(6, 4, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(7, 4, destSheet)));
+    CPPUNIT_ASSERT(m_pDoc->HasNote(ScAddress(8, 4, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 4, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(2, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(3, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(4, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(5, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(6, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(7, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(8, 5, destSheet)));
+    CPPUNIT_ASSERT(!m_pDoc->HasNote(ScAddress(9, 5, destSheet)));
+
+    // check values of notes
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(0, 0, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(3, 1, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(0, 1, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(3, 2, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(0, 2, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(3, 3, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(1, 0, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(4, 1, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(1, 2, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(4, 3, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(2, 1, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(5, 2, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(2, 2, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(5, 3, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(3, 0, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(6, 1, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(3, 1, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(6, 2, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(3, 2, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(6, 3, destSheet))->GetText());
+    if (!bSkipEmpty)
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(4, 2, srcSheet))->GetText(),
+                             m_pDoc->GetNote(ScAddress(7, 3, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(4, 3, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(7, 4, destSheet))->GetText());
+    if (!bSkipEmpty)
+        CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(5, 1, srcSheet))->GetText(),
+                             m_pDoc->GetNote(ScAddress(8, 2, destSheet))->GetText());
+    CPPUNIT_ASSERT_EQUAL(m_pDoc->GetNote(ScAddress(5, 3, srcSheet))->GetText(),
+                         m_pDoc->GetNote(ScAddress(8, 4, destSheet))->GetText());
+
+    m_pDoc->DeleteTab(destSheet);
+    m_pDoc->DeleteTab(srcSheet);
+}
+
+void Test::checkCopyPasteSpecialFiltered(bool bSkipEmpty)
+{
+    const SCTAB srcSheet = 0;
+    const SCTAB destSheet = 1;
+
+    /*
+         |  D  |    E     | F  |  G  |     H      |        I            |
+
+    2    | 1 B*| =D2+10  *| a  | R1 *| =D2+D4+60  | =SUMIF(D2:D5;"<4")  |
+    3    | 3 B*| =G3+30 b*| c *|  5 *|          B*|                     |
+    4    | 4   | =D2+40 b*| d *| R4 *| =D1+D3+70 *|    =E$1+$A$3+80    *|
+    5    | 1 B*| =D5+10  *| a  | R1 *| =D5+D7+60  | =SUMIF(D5:D8;"<4")  |   <- repeated row
+
+    * means note attached
+    B means background
+    b means border
+    */
+
+    OUString aString;
+    double fValue;
+    const EditTextObject* pEditObj;
+
+    // col 2
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 1, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 3, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 4, destSheet));
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(2, 5, destSheet));
+    // col 3, numbers
+    ASSERT_DOUBLES_EQUAL(1000, m_pDoc->GetValue(3, 0, destSheet));
+    ASSERT_DOUBLES_EQUAL(1, m_pDoc->GetValue(3, 1, destSheet));
+    ASSERT_DOUBLES_EQUAL(3, m_pDoc->GetValue(3, 2, destSheet));
+    ASSERT_DOUBLES_EQUAL(4, m_pDoc->GetValue(3, 3, destSheet));

... etc. - the rest is truncated


More information about the Libreoffice-commits mailing list