lok: re-factor getTextSelection.

Change-Id: I2c27c213ee980e19d6020e9599b2b72115e7f28e
Reviewed-on: https://gerrit.libreoffice.org/73626
Tested-by: Jenkins
Reviewed-by: Michael Meeks <michael.meeks@collabora.com>
diff --git a/desktop/source/lib/init.cxx b/desktop/source/lib/init.cxx
index da37be0..7c1a953 100644
--- a/desktop/source/lib/init.cxx
+++ b/desktop/source/lib/init.cxx
@@ -77,6 +77,7 @@
#include <com/sun/star/ucb/XUniversalContentBroker.hpp>
#include <com/sun/star/util/URLTransformer.hpp>
#include <com/sun/star/datatransfer/clipboard/XClipboard.hpp>
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
#include <com/sun/star/text/TextContentAnchorType.hpp>
#include <com/sun/star/document/XRedlinesSupplier.hpp>
#include <com/sun/star/ui/GlobalAcceleratorConfiguration.hpp>
@@ -3248,6 +3249,71 @@ static void doc_setTextSelection(LibreOfficeKitDocument* pThis, int nType, int n
    pDoc->setTextSelection(nType, nX, nY);
}

static bool getFromTransferrable(
    const css::uno::Reference<css::datatransfer::XTransferable> &xTransferable,
    const char *pMimeType, OString &aRet)
{
    // Take care of UTF-8 text here.
    OString aMimeType(pMimeType);
    bool bConvert = false;
    sal_Int32 nIndex = 0;
    if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
    {
        if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
        {
            aMimeType = "text/plain;charset=utf-16";
            bConvert = true;
        }
    }

    datatransfer::DataFlavor aFlavor;
    aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
    if (aMimeType == "text/plain;charset=utf-16")
        aFlavor.DataType = cppu::UnoType<OUString>::get();
    else
        aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();

    if (!xTransferable->isDataFlavorSupported(aFlavor))
    {
        SetLastExceptionMsg("Flavor " + aFlavor.MimeType + " is not supported");
        return false;
    }

    uno::Any aAny;
    try
    {
        aAny = xTransferable->getTransferData(aFlavor);
    }
    catch (const css::datatransfer::UnsupportedFlavorException& e)
    {
        SetLastExceptionMsg("Unsupported flavor " + aFlavor.MimeType + " exception " + e.Message);
        return false;
    }
    catch (const css::uno::Exception& e)
    {
        SetLastExceptionMsg("Exception getting " + aFlavor.MimeType + " exception " + e.Message);
        return false;
    }

    if (aFlavor.DataType == cppu::UnoType<OUString>::get())
    {
        OUString aString;
        aAny >>= aString;
        if (bConvert)
            aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
        else
            aRet = OString(reinterpret_cast<const sal_Char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
    }
    else
    {
        uno::Sequence<sal_Int8> aSequence;
        aAny >>= aSequence;
        aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
    }

    return true;;
}

static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMimeType, char** pUsedMimeType)
{
    comphelper::ProfileZone aZone("doc_getTextSelection");
@@ -3262,19 +3328,35 @@ static char* doc_getTextSelection(LibreOfficeKitDocument* pThis, const char* pMi
        return nullptr;
    }

    OString aUsedMimeType;
    OString aRet = pDoc->getTextSelection(pMimeType, aUsedMimeType);
    if (aUsedMimeType.isEmpty())
        aRet = pDoc->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    css::uno::Reference<css::datatransfer::XTransferable> xTransferable = pDoc->getSelection();
    if (!xTransferable)
    {
        SetLastExceptionMsg("No selection available");
        return nullptr;
    }

    const char *pType = pMimeType;
    if (!pType || pType[0] == '\0')
        pType = "text/plain;charset=utf-8";

    OString aRet;
    bool bSuccess = getFromTransferrable(xTransferable, pType, aRet);
    if (!bSuccess)
        return nullptr;

    char* pMemory = static_cast<char*>(malloc(aRet.getLength() + 1));
    assert(pMemory); // Don't handle OOM conditions
    strcpy(pMemory, aRet.getStr());

    if (pUsedMimeType)
    if (pUsedMimeType) // legacy
    {
        *pUsedMimeType = static_cast<char*>(malloc(aUsedMimeType.getLength() + 1));
        strcpy(*pUsedMimeType, aUsedMimeType.getStr());
        if (pMimeType)
        {
            *pUsedMimeType = static_cast<char*>(malloc(strlen(pMimeType) + 1));
            strcpy(*pUsedMimeType, pMimeType);
        }
        else
            *pUsedMimeType = nullptr;
    }

    return pMemory;
diff --git a/include/test/helper/transferable.hxx b/include/test/helper/transferable.hxx
new file mode 100644
index 0000000..280b28a
--- /dev/null
+++ b/include/test/helper/transferable.hxx
@@ -0,0 +1,30 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#pragma once

#include <com/sun/star/uno/XInterface.hpp>
#include <com/sun/star/uno/Reference.hxx>
#include <com/sun/star/datatransfer/XTransferable.hpp>
#include <rtl/string.hxx>
#include <test/testdllapi.hxx>

namespace apitest
{
namespace helper
{
namespace transferable
{
OString OOO_DLLPUBLIC_TEST getTextSelection(
    const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable, OString mimeType);
} // namespace transferable
} // namespace helper
} // namespace apitest

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */
diff --git a/include/vcl/ITiledRenderable.hxx b/include/vcl/ITiledRenderable.hxx
index 9ab4f57..e95e2bd 100644
--- a/include/vcl/ITiledRenderable.hxx
+++ b/include/vcl/ITiledRenderable.hxx
@@ -17,6 +17,7 @@
#include <vcl/ptrstyle.hxx>
#include <vcl/vclptr.hxx>
#include <map>
#include <com/sun/star/datatransfer/XTransferable.hpp>

namespace com::sun::star::beans { struct PropertyValue; }
namespace com::sun::star::datatransfer { namespace clipboard { class XClipboard; } }
@@ -142,11 +143,9 @@ public:
    virtual void setTextSelection(int nType, int nX, int nY) = 0;

    /**
     * Gets the text selection.
     *
     * @see lok::Document::getTextSelection().
     * Gets the selection as a transferable for later processing
     */
    virtual OString getTextSelection(const char* pMimeType, OString& rUsedMimeType) = 0;
    virtual css::uno::Reference<css::datatransfer::XTransferable> getSelection() = 0;

    /**
     * Adjusts the graphic selection.
diff --git a/sc/inc/docuno.hxx b/sc/inc/docuno.hxx
index 5803796..501028b 100644
--- a/sc/inc/docuno.hxx
+++ b/sc/inc/docuno.hxx
@@ -343,8 +343,8 @@ public:
    /// @see vcl::ITiledRenderable::setTextSelection().
    virtual void setTextSelection(int nType, int nX, int nY) override;

    /// @see vcl::ITiledRenderable::getTextSelection().
    virtual OString getTextSelection(const char* pMimeType, OString& rUsedMimeType) override;
    /// @see vcl::ITiledRenderable::getSelection().
    virtual css::uno::Reference<css::datatransfer::XTransferable> getSelection() override;

    /// @see vcl::ITiledRenderable::setGraphicSelection().
    virtual void setGraphicSelection(int nType, int nX, int nY) override;
diff --git a/sc/qa/unit/tiledrendering/tiledrendering.cxx b/sc/qa/unit/tiledrendering/tiledrendering.cxx
index 8bc9819..44d090b6 100644
--- a/sc/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sc/qa/unit/tiledrendering/tiledrendering.cxx
@@ -8,6 +8,7 @@
 */

#include <test/bootstrapfixture.hxx>
#include <test/helper/transferable.hxx>
#include <unotest/macros_test.hxx>
#include <test/xmltesttools.hxx>
#include <boost/property_tree/json_parser.hpp>
@@ -16,6 +17,7 @@
#include <com/sun/star/frame/Desktop.hpp>
#include <com/sun/star/frame/DispatchHelper.hpp>
#include <com/sun/star/datatransfer/clipboard/SystemClipboard.hpp>
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/propertysequence.hxx>
@@ -33,6 +35,7 @@
#include <vcl/scheduler.hxx>
#include <vcl/vclevent.hxx>
#include <vcl/virdev.hxx>
#include <vcl/unohelp2.hxx>
#include <sc.hrc>

#include <chrono>
@@ -253,8 +256,7 @@ void ScTiledRenderingTest::testRowColumnSelections()
    comphelper::dispatchCommand(".uno:SelectRow", aArgs);

    // Check if it is selected
    OString aUsedMimeType;
    OString aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    OString aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
    OString aExpected("1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n");
    CPPUNIT_ASSERT_EQUAL(aExpected, aResult);

@@ -266,7 +268,7 @@ void ScTiledRenderingTest::testRowColumnSelections()
    comphelper::dispatchCommand(".uno:SelectRow", aArgs);

    // Check if all the rows from 5th to 10th get selected
    aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
    aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\n3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\n4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\n5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\n6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\t22\t23\t24\t25\t26\n";
    CPPUNIT_ASSERT_EQUAL(aExpected, aResult);

@@ -279,7 +281,7 @@ void ScTiledRenderingTest::testRowColumnSelections()

    // When we copy this, we don't get anything useful, but we must not crash
    // (used to happen)
    aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
    CPPUNIT_ASSERT_EQUAL(OString(), aResult);

    // TODO check that we really selected what we wanted here
@@ -293,7 +295,7 @@ void ScTiledRenderingTest::testRowColumnSelections()

    // When we copy this, we don't get anything useful, but we must not crash
    // (used to happen)
    aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
    CPPUNIT_ASSERT_EQUAL(OString(), aResult);

    // TODO check that we really selected what we wanted here
@@ -321,7 +323,7 @@ void ScTiledRenderingTest::testRowColumnSelections()
    comphelper::dispatchCommand(".uno:SelectRow", aArgs);

    //  only row 5 should remain selected
    aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    aResult = apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8");
    aExpected = "1\t2\t3\t4\t5\t6\t7\t8\t9\t10\t11\t12\t13\t14\t15\t16\t17\t18\t19\t20\t21\n";
    CPPUNIT_ASSERT_EQUAL(aExpected, aResult);
}
@@ -420,11 +422,8 @@ void ScTiledRenderingTest::testEmptyColumnSelection()
        }));
    comphelper::dispatchCommand(".uno:SelectColumn", aArgs);

    // Get plain selection
    OString aUsedMimeType;
    OString aResult = pModelObj->getTextSelection("text/plain;charset=utf-8", aUsedMimeType);
    // should be an empty string
    CPPUNIT_ASSERT_EQUAL(OString(), aResult);
    CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pModelObj->getSelection(), "text/plain;charset=utf-8"));
}

/// A view callback tracks callbacks invoked on one specific view.
diff --git a/sc/source/ui/unoobj/docuno.cxx b/sc/source/ui/unoobj/docuno.cxx
index 9989eb8..3c399a6 100644
--- a/sc/source/ui/unoobj/docuno.cxx
+++ b/sc/source/ui/unoobj/docuno.cxx
@@ -776,7 +776,7 @@ void ScModelObj::setTextSelection(int nType, int nX, int nY)
    }
}

OString ScModelObj::getTextSelection(const char* pMimeType, OString& rUsedMimeType)
uno::Reference<datatransfer::XTransferable> ScModelObj::getSelection()
{
    SolarMutexGuard aGuard;

@@ -804,64 +804,7 @@ OString ScModelObj::getTextSelection(const char* pMimeType, OString& rUsedMimeTy
    if (!xTransferable.is())
        xTransferable.set( aDataHelper.GetTransferable() );

    // Take care of UTF-8 text here.
    OString aMimeType(pMimeType);
    bool bConvert = false;
    sal_Int32 nIndex = 0;
    if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
    {
        if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
        {
            aMimeType = "text/plain;charset=utf-16";
            bConvert = true;
        }
    }

    datatransfer::DataFlavor aFlavor;
    aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
    if (aMimeType == "text/plain;charset=utf-16")
        aFlavor.DataType = cppu::UnoType<OUString>::get();
    else
        aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();

    if (!xTransferable.is() || !xTransferable->isDataFlavorSupported(aFlavor))
        return OString();

    uno::Any aAny;
    try
    {
        aAny = xTransferable->getTransferData(aFlavor);
    }
    catch (const datatransfer::UnsupportedFlavorException& e)
    {
        SAL_WARN("sc", "Caught " << e);
        return OString();
    }
    catch (const css::uno::Exception& e)
    {
        SAL_WARN("sc", "Caught " << e);
        return OString();
    }

    OString aRet;
    if (aFlavor.DataType == cppu::UnoType<OUString>::get())
    {
        OUString aString;
        aAny >>= aString;
        if (bConvert)
            aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
        else
            aRet = OString(reinterpret_cast<const sal_Char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
    }
    else
    {
        uno::Sequence<sal_Int8> aSequence;
        aAny >>= aSequence;
        aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
    }

    rUsedMimeType = pMimeType;
    return aRet;
    return xTransferable;
}

void ScModelObj::setGraphicSelection(int nType, int nX, int nY)
diff --git a/sd/qa/unit/tiledrendering/tiledrendering.cxx b/sd/qa/unit/tiledrendering/tiledrendering.cxx
index c5f00e3..731a143 100644
--- a/sd/qa/unit/tiledrendering/tiledrendering.cxx
+++ b/sd/qa/unit/tiledrendering/tiledrendering.cxx
@@ -10,6 +10,7 @@
#include "../sdmodeltestbase.hxx"
#include <app.hrc>
#include <test/bootstrapfixture.hxx>
#include <test/helper/transferable.hxx>
#include <unotest/macros_test.hxx>
#include <test/xmltesttools.hxx>
#include <boost/property_tree/json_parser.hpp>
@@ -483,11 +484,10 @@ void SdTiledRenderingTest::testGetTextSelection()
    ESelection aWordSelection(0, 0, 0, 5);
    rEditView.SetSelection(aWordSelection);
    // Did we indeed manage to copy the selected text?
    OString aUsedFormat;
    CPPUNIT_ASSERT_EQUAL(OString("Shape"), pXImpressDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString("Shape"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));

    // Make sure returned RTF is not empty.
    CPPUNIT_ASSERT(!pXImpressDocument->getTextSelection("text/rtf", aUsedFormat).isEmpty());
    CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/rtf").isEmpty());
    comphelper::LibreOfficeKit::setActive(false);
}

@@ -633,9 +633,8 @@ void SdTiledRenderingTest::testSearchAll()

    lcl_search("match", /*bFindAll=*/true);

    OString aUsedFormat;
    // This was empty: find-all did not highlight the first match.
    CPPUNIT_ASSERT_EQUAL(OString("match"), pXImpressDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString("match"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));

    // We're on the first slide, search for something on the second slide and make sure we get a SET_PART.
    m_nPart = 0;
@@ -685,10 +684,9 @@ void SdTiledRenderingTest::testSearchAllFollowedBySearch()
    lcl_search("third", /*bFindAll=*/true);
    lcl_search("match" /*,bFindAll=false*/);

    OString aUsedFormat;
    // This used to give wrong result: 'search' after 'search all' still
    // returned 'third'
    CPPUNIT_ASSERT_EQUAL(OString("match"), pXImpressDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString("match"), apitest::helper::transferable::getTextSelection(pXImpressDocument->getSelection(), "text/plain;charset=utf-8"));
    comphelper::LibreOfficeKit::setActive(false);
}

diff --git a/sd/source/ui/inc/ViewShell.hxx b/sd/source/ui/inc/ViewShell.hxx
index dea7dfb..c8fce20 100644
--- a/sd/source/ui/inc/ViewShell.hxx
+++ b/sd/source/ui/inc/ViewShell.hxx
@@ -412,8 +412,8 @@ public:

    /// Allows adjusting the point or mark of the selection to a document coordinate.
    void SetCursorMm100Position(const Point& rPosition, bool bPoint, bool bClearMark);
    /// Gets the currently selected text.
    OString GetTextSelection(const OString& aMimeType, OString& rUsedMimeType);
    /// Gets the current selectiion
    css::uno::Reference<css::datatransfer::XTransferable> GetSelectionTransferrable();
    /// Allows starting or ending a graphic move or resize action.
    void SetGraphicMm100Position(bool bStart, const Point& rPosition);

diff --git a/sd/source/ui/inc/unomodel.hxx b/sd/source/ui/inc/unomodel.hxx
index d13109d..2be081b 100644
--- a/sd/source/ui/inc/unomodel.hxx
+++ b/sd/source/ui/inc/unomodel.hxx
@@ -249,8 +249,8 @@ public:
    virtual void postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) override;
    /// @see vcl::ITiledRenderable::setTextSelection().
    virtual void setTextSelection(int nType, int nX, int nY) override;
    /// @see vcl::ITiledRenderable::getTextSelection().
    virtual OString getTextSelection(const char* pMimeType, OString& rUsedMimeType) override;
    /// @see vcl::ITiledRenderable::getSelection().
    virtual css::uno::Reference<css::datatransfer::XTransferable> getSelection() override;
    /// @see vcl::ITiledRenderable::setGraphicSelection().
    virtual void setGraphicSelection(int nType, int nX, int nY) override;
    /// @see lok::Document::resetSelection().
diff --git a/sd/source/ui/unoidl/unomodel.cxx b/sd/source/ui/unoidl/unomodel.cxx
index 237dab1..e6c15f7 100644
--- a/sd/source/ui/unoidl/unomodel.cxx
+++ b/sd/source/ui/unoidl/unomodel.cxx
@@ -2538,15 +2538,15 @@ void SdXImpressDocument::setTextSelection(int nType, int nX, int nY)
    }
}

OString SdXImpressDocument::getTextSelection(const char* pMimeType, OString& rUsedMimeType)
uno::Reference<datatransfer::XTransferable> SdXImpressDocument::getSelection()
{
    SolarMutexGuard aGuard;

    DrawViewShell* pViewShell = GetViewShell();
    if (!pViewShell)
        return OString();
        return uno::Reference<datatransfer::XTransferable>();

    return pViewShell->GetTextSelection(pMimeType, rUsedMimeType);
    return pViewShell->GetSelectionTransferrable();
}

void SdXImpressDocument::setGraphicSelection(int nType, int nX, int nY)
diff --git a/sd/source/ui/view/viewshel.cxx b/sd/source/ui/view/viewshel.cxx
index 8679f06..f2c1a56 100644
--- a/sd/source/ui/view/viewshel.cxx
+++ b/sd/source/ui/view/viewshel.cxx
@@ -524,62 +524,17 @@ void ViewShell::SetCursorMm100Position(const Point& rPosition, bool bPoint, bool
    }
}

OString ViewShell::GetTextSelection(const OString& _aMimeType, OString& rUsedMimeType)
uno::Reference<datatransfer::XTransferable> ViewShell::GetSelectionTransferrable()
{
    SdrView* pSdrView = GetView();
    if (!pSdrView)
        return OString();
        return uno::Reference<datatransfer::XTransferable>();

    if (!pSdrView->GetTextEditObject())
        return OString();
        return uno::Reference<datatransfer::XTransferable>();

    EditView& rEditView = pSdrView->GetTextEditOutlinerView()->GetEditView();
    uno::Reference<datatransfer::XTransferable> xTransferable = rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection());

    // Take care of UTF-8 text here.
    bool bConvert = false;
    sal_Int32 nIndex = 0;
    OString aMimeType = _aMimeType;
    if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
    {
        if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
        {
            aMimeType = "text/plain;charset=utf-16";
            bConvert = true;
        }
    }

    datatransfer::DataFlavor aFlavor;
    aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
    if (bConvert || aMimeType == "text/plain;charset=utf-16")
        aFlavor.DataType = cppu::UnoType<OUString>::get();
    else
        aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();

    if (!xTransferable->isDataFlavorSupported(aFlavor))
        return OString();

    uno::Any aAny(xTransferable->getTransferData(aFlavor));

    OString aRet;
    if (aFlavor.DataType == cppu::UnoType<OUString>::get())
    {
        OUString aString;
        aAny >>= aString;
        if (bConvert)
            aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
        else
            aRet = OString(reinterpret_cast<const sal_Char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
    }
    else
    {
        uno::Sequence<sal_Int8> aSequence;
        aAny >>= aSequence;
        aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
    }

    rUsedMimeType = _aMimeType;
    return aRet;
    return rEditView.GetEditEngine()->CreateTransferable(rEditView.GetSelection());
}

void ViewShell::SetGraphicMm100Position(bool bStart, const Point& rPosition)
diff --git a/sw/inc/unotxdoc.hxx b/sw/inc/unotxdoc.hxx
index 7c956e3..564f5c1 100644
--- a/sw/inc/unotxdoc.hxx
+++ b/sw/inc/unotxdoc.hxx
@@ -418,8 +418,8 @@ public:
    virtual void postMouseEvent(int nType, int nX, int nY, int nCount, int nButtons, int nModifier) override;
    /// @see vcl::ITiledRenderable::setTextSelection().
    virtual void setTextSelection(int nType, int nX, int nY) override;
    /// @see vcl::ITiledRenderable::getTextSelection().
    virtual OString getTextSelection(const char* pMimeType, OString& rUsedMimeType) override;
    /// @see vcl::ITiledRenderable::getSelection().
    virtual css::uno::Reference<css::datatransfer::XTransferable> getSelection() override;
    /// @see vcl::ITiledRenderable::setGraphicSelection().
    virtual void setGraphicSelection(int nType, int nX, int nY) override;
    /// @see vcl::ITiledRenderable::resetSelection().
diff --git a/sw/qa/extras/tiledrendering/tiledrendering.cxx b/sw/qa/extras/tiledrendering/tiledrendering.cxx
index b7d90cb..965a64f 100644
--- a/sw/qa/extras/tiledrendering/tiledrendering.cxx
+++ b/sw/qa/extras/tiledrendering/tiledrendering.cxx
@@ -13,6 +13,7 @@
#include <com/sun/star/frame/DispatchResultState.hpp>
#include <com/sun/star/frame/XDispatchResultListener.hpp>
#include <swmodeltestbase.hxx>
#include <test/helper/transferable.hxx>
#include <LibreOfficeKit/LibreOfficeKitEnums.h>
#include <comphelper/dispatchcommand.hxx>
#include <comphelper/propertysequence.hxx>
@@ -406,8 +407,7 @@ void SwTiledRenderingTest::testGetTextSelection()

    SwXTextDocument* pXTextDocument = createDoc("shape-with-text.fodt");
    // No crash, just empty output for unexpected mime type.
    OString aUsedFormat;
    CPPUNIT_ASSERT_EQUAL(OString(), pXTextDocument->getTextSelection("foo/bar", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString(), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "foo/bar"));

    SwWrtShell* pWrtShell = pXTextDocument->GetDocShell()->GetWrtShell();
    // Move the cursor into the first word.
@@ -416,10 +416,10 @@ void SwTiledRenderingTest::testGetTextSelection()
    pWrtShell->SelWrd();

    // Make sure that we selected text from the body text.
    CPPUNIT_ASSERT_EQUAL(OString("Hello"), pXTextDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString("Hello"), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"));

    // Make sure we produce something for HTML.
    CPPUNIT_ASSERT(!pXTextDocument->getTextSelection("text/html", aUsedFormat).isEmpty());
    CPPUNIT_ASSERT(!apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/html").isEmpty());

    // Now select some shape text and check again.
    SdrPage* pPage = pWrtShell->GetDoc()->getIDocumentDrawModelAccess().GetDrawModel()->GetPage(0);
@@ -430,7 +430,7 @@ void SwTiledRenderingTest::testGetTextSelection()
    EditView& rEditView = pView->GetTextEditOutlinerView()->GetEditView();
    ESelection aWordSelection(0, 0, 0, 5);
    rEditView.SetSelection(aWordSelection);
    CPPUNIT_ASSERT_EQUAL(OString("Shape"), pXTextDocument->getTextSelection("text/plain;charset=utf-8", aUsedFormat));
    CPPUNIT_ASSERT_EQUAL(OString("Shape"), apitest::helper::transferable::getTextSelection(pXTextDocument->getSelection(), "text/plain;charset=utf-8"));
}

void SwTiledRenderingTest::testSetGraphicSelection()
diff --git a/sw/source/uibase/uno/unotxdoc.cxx b/sw/source/uibase/uno/unotxdoc.cxx
index a950744..a71a8a9 100644
--- a/sw/source/uibase/uno/unotxdoc.cxx
+++ b/sw/source/uibase/uno/unotxdoc.cxx
@@ -3575,7 +3575,7 @@ void SwXTextDocument::setTextSelection(int nType, int nX, int nY)
    }
}

OString SwXTextDocument::getTextSelection(const char* pMimeType, OString& rUsedMimeType)
uno::Reference<datatransfer::XTransferable> SwXTextDocument::getSelection()
{
    SolarMutexGuard aGuard;

@@ -3605,50 +3605,7 @@ OString SwXTextDocument::getTextSelection(const char* pMimeType, OString& rUsedM
    if (!xTransferable.is())
        xTransferable = new SwTransferable(*pWrtShell);

    // Take care of UTF-8 text here.
    OString aMimeType(pMimeType);
    bool bConvert = false;
    sal_Int32 nIndex = 0;
    if (aMimeType.getToken(0, ';', nIndex) == "text/plain")
    {
        if (aMimeType.getToken(0, ';', nIndex) == "charset=utf-8")
        {
            aMimeType = "text/plain;charset=utf-16";
            bConvert = true;
        }
    }

    datatransfer::DataFlavor aFlavor;
    aFlavor.MimeType = OUString::fromUtf8(aMimeType.getStr());
    if (aMimeType == "text/plain;charset=utf-16")
        aFlavor.DataType = cppu::UnoType<OUString>::get();
    else
        aFlavor.DataType = cppu::UnoType< uno::Sequence<sal_Int8> >::get();

    if (!xTransferable->isDataFlavorSupported(aFlavor))
        return OString();

    uno::Any aAny(xTransferable->getTransferData(aFlavor));

    OString aRet;
    if (aFlavor.DataType == cppu::UnoType<OUString>::get())
    {
        OUString aString;
        aAny >>= aString;
        if (bConvert)
            aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
        else
            aRet = OString(reinterpret_cast<const sal_Char *>(aString.getStr()), aString.getLength() * sizeof(sal_Unicode));
    }
    else
    {
        uno::Sequence<sal_Int8> aSequence;
        aAny >>= aSequence;
        aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
    }

    rUsedMimeType = pMimeType;
    return aRet;
    return xTransferable;
}

void SwXTextDocument::setGraphicSelection(int nType, int nX, int nY)
diff --git a/test/Library_test.mk b/test/Library_test.mk
index ce318d9..4be0598 100644
--- a/test/Library_test.mk
+++ b/test/Library_test.mk
@@ -52,6 +52,7 @@ $(eval $(call gb_Library_add_exception_objects,test,\
    test/source/unoapi_property_testers \
	test/source/helper/form \
	test/source/helper/shape \
	test/source/helper/transferable \
))

# vim: set noet sw=4 ts=4:
diff --git a/test/source/helper/transferable.cxx b/test/source/helper/transferable.cxx
new file mode 100644
index 0000000..ec182f5
--- /dev/null
+++ b/test/source/helper/transferable.cxx
@@ -0,0 +1,87 @@
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <test/helper/transferable.hxx>
#include <com/sun/star/datatransfer/UnsupportedFlavorException.hpp>

using namespace css;

namespace apitest
{
namespace helper
{
namespace transferable
{
OString OOO_DLLPUBLIC_TEST getTextSelection(
    const css::uno::Reference<css::datatransfer::XTransferable>& xTransferable, OString mimeType)
{
    if (!xTransferable.is())
        return OString();

    // Take care of UTF-8 text here.
    bool bConvert = false;
    sal_Int32 nIndex = 0;
    if (mimeType.getToken(0, ';', nIndex) == "text/plain")
    {
        if (mimeType.getToken(0, ';', nIndex) == "charset=utf-8")
        {
            mimeType = "text/plain;charset=utf-16";
            bConvert = true;
        }
    }

    datatransfer::DataFlavor aFlavor;
    aFlavor.MimeType = OUString::fromUtf8(mimeType.getStr());
    if (mimeType == "text/plain;charset=utf-16")
        aFlavor.DataType = cppu::UnoType<OUString>::get();
    else
        aFlavor.DataType = cppu::UnoType<uno::Sequence<sal_Int8>>::get();

    if (!xTransferable.is() || !xTransferable->isDataFlavorSupported(aFlavor))
        return OString();

    uno::Any aAny;
    try
    {
        aAny = xTransferable->getTransferData(aFlavor);
    }
    catch (const css::datatransfer::UnsupportedFlavorException&)
    {
        return OString();
    }
    catch (const css::uno::Exception&)
    {
        return OString();
    }

    OString aRet;
    if (aFlavor.DataType == cppu::UnoType<OUString>::get())
    {
        OUString aString;
        aAny >>= aString;
        if (bConvert)
            aRet = OUStringToOString(aString, RTL_TEXTENCODING_UTF8);
        else
            aRet = OString(reinterpret_cast<const sal_Char*>(aString.getStr()),
                           aString.getLength() * sizeof(sal_Unicode));
    }
    else
    {
        uno::Sequence<sal_Int8> aSequence;
        aAny >>= aSequence;
        aRet = OString(reinterpret_cast<sal_Char*>(aSequence.getArray()), aSequence.getLength());
    }
    return aRet;
}

} // namespace transferable
} // namespace helper
} // namespace apitest

/* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */