2018-09-14 14:53:40 +02:00
|
|
|
/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
|
|
|
|
/*
|
|
|
|
* This file is part of the LibreOffice project.
|
|
|
|
*
|
|
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
|
|
|
#include <comphelper/solarmutex.hxx>
|
|
|
|
#include <comphelper/sequence.hxx>
|
|
|
|
#include <cppuhelper/supportsservice.hxx>
|
|
|
|
#include <vcl/svapp.hxx>
|
|
|
|
|
2018-09-18 12:23:34 +02:00
|
|
|
#include <QtWidgets/QApplication>
|
|
|
|
#include <QtGui/QClipboard>
|
2018-10-09 15:33:12 +02:00
|
|
|
#include <QtCore/QBuffer>
|
2018-10-08 09:41:37 +02:00
|
|
|
#include <QtCore/QMimeData>
|
2018-09-18 12:23:34 +02:00
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
#include <Qt5Clipboard.hxx>
|
2018-09-18 12:23:34 +02:00
|
|
|
#include <Qt5Tools.hxx>
|
2018-10-10 13:33:15 +02:00
|
|
|
namespace
|
|
|
|
{
|
|
|
|
void lcl_peekFormats(const css::uno::Sequence<css::datatransfer::DataFlavor>& rFormats,
|
|
|
|
bool& bHasHtml, bool& bHasImage)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < rFormats.getLength(); ++i)
|
|
|
|
{
|
|
|
|
const css::datatransfer::DataFlavor& rFlavor = rFormats[i];
|
|
|
|
|
|
|
|
if (rFlavor.MimeType == "text/html")
|
|
|
|
bHasHtml = true;
|
|
|
|
else if (rFlavor.MimeType.startsWith("image"))
|
|
|
|
bHasImage = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-09-14 14:53:40 +02:00
|
|
|
|
|
|
|
std::vector<css::datatransfer::DataFlavor> Qt5Transferable::getTransferDataFlavorsAsVector()
|
|
|
|
{
|
|
|
|
std::vector<css::datatransfer::DataFlavor> aVector;
|
|
|
|
|
2018-10-08 09:41:37 +02:00
|
|
|
const QClipboard* clipboard = QApplication::clipboard();
|
|
|
|
const QMimeData* mimeData = clipboard->mimeData();
|
2018-09-14 14:53:40 +02:00
|
|
|
css::datatransfer::DataFlavor aFlavor;
|
2018-10-08 09:41:37 +02:00
|
|
|
|
2018-12-12 10:03:49 +01:00
|
|
|
if (mimeData)
|
2018-10-08 09:41:37 +02:00
|
|
|
{
|
2018-12-12 10:03:49 +01:00
|
|
|
for (QString& rMimeType : mimeData->formats())
|
|
|
|
{
|
|
|
|
// filter out non-MIME types such as TARGETS, MULTIPLE, TIMESTAMP
|
|
|
|
if (rMimeType.indexOf('/') == -1)
|
|
|
|
continue;
|
|
|
|
|
|
|
|
if (rMimeType.startsWith("text/plain"))
|
|
|
|
{
|
|
|
|
aFlavor.MimeType = "text/plain;charset=utf-16";
|
|
|
|
aFlavor.DataType = cppu::UnoType<OUString>::get();
|
|
|
|
aVector.push_back(aFlavor);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
aFlavor.MimeType = toOUString(rMimeType);
|
|
|
|
aFlavor.DataType = cppu::UnoType<Sequence<sal_Int8>>::get();
|
|
|
|
aVector.push_back(aFlavor);
|
|
|
|
}
|
|
|
|
}
|
2018-10-09 15:33:12 +02:00
|
|
|
}
|
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
return aVector;
|
|
|
|
}
|
|
|
|
|
|
|
|
css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL Qt5Transferable::getTransferDataFlavors()
|
|
|
|
{
|
|
|
|
return comphelper::containerToSequence(getTransferDataFlavorsAsVector());
|
|
|
|
}
|
|
|
|
|
|
|
|
sal_Bool SAL_CALL
|
2018-12-12 10:03:49 +01:00
|
|
|
Qt5Transferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
|
2018-09-14 14:53:40 +02:00
|
|
|
{
|
|
|
|
const std::vector<css::datatransfer::DataFlavor> aAll = getTransferDataFlavorsAsVector();
|
|
|
|
|
2018-12-12 10:03:49 +01:00
|
|
|
return std::any_of(aAll.begin(), aAll.end(), [&](const css::datatransfer::DataFlavor& aFlavor) {
|
|
|
|
return rFlavor.MimeType == aFlavor.MimeType;
|
|
|
|
}); //FIXME
|
2018-09-14 14:53:40 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
|
|
|
* XTransferable
|
|
|
|
*/
|
|
|
|
|
|
|
|
css::uno::Any SAL_CALL
|
2018-09-18 12:23:34 +02:00
|
|
|
Qt5Transferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
|
2018-09-14 14:53:40 +02:00
|
|
|
{
|
|
|
|
css::uno::Any aRet;
|
2018-10-08 09:41:37 +02:00
|
|
|
const QClipboard* clipboard = QApplication::clipboard();
|
|
|
|
const QMimeData* mimeData = clipboard->mimeData();
|
2018-09-18 12:23:34 +02:00
|
|
|
|
|
|
|
if (rFlavor.MimeType == "text/plain;charset=utf-16")
|
|
|
|
{
|
2018-10-08 09:41:37 +02:00
|
|
|
QString clipboardContent = mimeData->text();
|
2018-09-18 12:23:34 +02:00
|
|
|
OUString sContent = toOUString(clipboardContent);
|
|
|
|
|
|
|
|
aRet <<= sContent.replaceAll("\r\n", "\n");
|
|
|
|
}
|
2018-10-08 09:41:37 +02:00
|
|
|
else if (rFlavor.MimeType == "text/html")
|
|
|
|
{
|
|
|
|
QString clipboardContent = mimeData->html();
|
|
|
|
std::string aStr = clipboardContent.toStdString();
|
|
|
|
Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(aStr.c_str()), aStr.length());
|
|
|
|
aRet <<= aSeq;
|
2018-10-09 15:33:12 +02:00
|
|
|
}
|
|
|
|
else if (rFlavor.MimeType.startsWith("image") && mimeData->hasImage())
|
|
|
|
{
|
|
|
|
QImage image = qvariant_cast<QImage>(mimeData->imageData());
|
|
|
|
QByteArray ba;
|
|
|
|
QBuffer buffer(&ba);
|
|
|
|
sal_Int32 nIndex = rFlavor.MimeType.indexOf('/');
|
|
|
|
OUString sFormat(nIndex != -1 ? rFlavor.MimeType.copy(nIndex + 1) : "png");
|
|
|
|
|
|
|
|
buffer.open(QIODevice::WriteOnly);
|
|
|
|
image.save(&buffer, sFormat.toUtf8().getStr());
|
|
|
|
|
|
|
|
Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(ba.data()), ba.size());
|
|
|
|
aRet <<= aSeq;
|
2018-10-08 09:41:37 +02:00
|
|
|
}
|
2018-09-18 12:23:34 +02:00
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
return aRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
VclQt5Clipboard::VclQt5Clipboard()
|
|
|
|
: cppu::WeakComponentImplHelper<datatransfer::clipboard::XSystemClipboard,
|
|
|
|
datatransfer::clipboard::XFlushableClipboard, XServiceInfo>(
|
|
|
|
m_aMutex)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
void VclQt5Clipboard::flushClipboard()
|
|
|
|
{
|
|
|
|
SolarMutexGuard aGuard;
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
VclQt5Clipboard::~VclQt5Clipboard() {}
|
|
|
|
|
|
|
|
OUString VclQt5Clipboard::getImplementationName()
|
|
|
|
{
|
|
|
|
return OUString("com.sun.star.datatransfer.VclQt5Clipboard");
|
|
|
|
}
|
|
|
|
|
|
|
|
Sequence<OUString> VclQt5Clipboard::getSupportedServiceNames()
|
|
|
|
{
|
|
|
|
Sequence<OUString> aRet{ "com.sun.star.datatransfer.clipboard.SystemClipboard" };
|
|
|
|
return aRet;
|
|
|
|
}
|
|
|
|
|
|
|
|
sal_Bool VclQt5Clipboard::supportsService(const OUString& ServiceName)
|
|
|
|
{
|
|
|
|
return cppu::supportsService(this, ServiceName);
|
|
|
|
}
|
|
|
|
|
|
|
|
Reference<css::datatransfer::XTransferable> VclQt5Clipboard::getContents()
|
|
|
|
{
|
|
|
|
if (!m_aContents.is())
|
2018-10-04 11:09:48 +02:00
|
|
|
m_aContents = new Qt5Transferable;
|
2018-09-14 14:53:40 +02:00
|
|
|
|
|
|
|
return m_aContents;
|
|
|
|
}
|
|
|
|
|
|
|
|
void VclQt5Clipboard::setContents(
|
|
|
|
const Reference<css::datatransfer::XTransferable>& xTrans,
|
|
|
|
const Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
|
|
|
|
{
|
|
|
|
osl::ClearableMutexGuard aGuard(m_aMutex);
|
|
|
|
Reference<datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner);
|
|
|
|
Reference<datatransfer::XTransferable> xOldContents(m_aContents);
|
|
|
|
m_aContents = xTrans;
|
|
|
|
m_aOwner = xClipboardOwner;
|
|
|
|
|
|
|
|
std::vector<Reference<datatransfer::clipboard::XClipboardListener>> aListeners(m_aListeners);
|
|
|
|
datatransfer::clipboard::ClipboardEvent aEv;
|
|
|
|
|
2018-10-02 11:36:39 +02:00
|
|
|
if (m_aContents.is())
|
|
|
|
{
|
2018-10-10 13:33:15 +02:00
|
|
|
css::uno::Sequence<css::datatransfer::DataFlavor> aFormats
|
|
|
|
= xTrans->getTransferDataFlavors();
|
|
|
|
bool bHasHtml = false, bHasImage = false;
|
|
|
|
lcl_peekFormats(aFormats, bHasHtml, bHasImage);
|
|
|
|
|
2019-01-17 15:31:51 +03:00
|
|
|
std::unique_ptr<QMimeData> pMimeData(new QMimeData);
|
2018-10-10 13:33:15 +02:00
|
|
|
|
2019-01-17 15:31:51 +03:00
|
|
|
// Add html data if present
|
|
|
|
if (bHasHtml)
|
2018-10-10 13:33:15 +02:00
|
|
|
{
|
2019-01-17 15:31:51 +03:00
|
|
|
css::datatransfer::DataFlavor aFlavor;
|
2018-10-10 13:33:15 +02:00
|
|
|
aFlavor.MimeType = "text/html";
|
2019-01-17 15:31:51 +03:00
|
|
|
aFlavor.DataType = cppu::UnoType<Sequence<sal_Int8>>::get();
|
2018-10-02 11:36:39 +02:00
|
|
|
|
2019-01-17 15:31:51 +03:00
|
|
|
Any aValue;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
aValue = xTrans->getTransferData(aFlavor);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
2018-10-10 13:33:15 +02:00
|
|
|
|
2019-01-17 15:31:51 +03:00
|
|
|
if (aValue.getValueType() == cppu::UnoType<Sequence<sal_Int8>>::get())
|
2018-10-10 13:33:15 +02:00
|
|
|
{
|
2019-01-17 15:31:51 +03:00
|
|
|
Sequence<sal_Int8> aData;
|
|
|
|
aValue >>= aData;
|
|
|
|
|
2018-10-10 13:33:15 +02:00
|
|
|
OUString aHtmlAsString(reinterpret_cast<const char*>(aData.getConstArray()),
|
|
|
|
aData.getLength(), RTL_TEXTENCODING_UTF8);
|
|
|
|
|
2019-01-17 15:31:51 +03:00
|
|
|
pMimeData->setHtml(toQString(aHtmlAsString));
|
2018-10-10 13:33:15 +02:00
|
|
|
}
|
2019-01-17 15:31:51 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
// Add image data if present
|
|
|
|
if (bHasImage)
|
|
|
|
{
|
|
|
|
css::datatransfer::DataFlavor aFlavor;
|
|
|
|
//FIXME: other image formats?
|
|
|
|
aFlavor.MimeType = "image/png";
|
|
|
|
aFlavor.DataType = cppu::UnoType<Sequence<sal_Int8>>::get();
|
|
|
|
|
|
|
|
Any aValue;
|
|
|
|
try
|
2018-10-10 13:33:15 +02:00
|
|
|
{
|
2019-01-17 15:31:51 +03:00
|
|
|
aValue = xTrans->getTransferData(aFlavor);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aValue.getValueType() == cppu::UnoType<Sequence<sal_Int8>>::get())
|
|
|
|
{
|
|
|
|
Sequence<sal_Int8> aData;
|
|
|
|
aValue >>= aData;
|
|
|
|
|
2018-10-10 13:33:15 +02:00
|
|
|
QImage image;
|
|
|
|
image.loadFromData(reinterpret_cast<const uchar*>(aData.getConstArray()),
|
|
|
|
aData.getLength());
|
2019-01-17 15:31:51 +03:00
|
|
|
|
|
|
|
pMimeData->setImageData(image);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Add text data
|
|
|
|
// TODO: consider checking if text of suitable type is present
|
|
|
|
{
|
|
|
|
css::datatransfer::DataFlavor aFlavor;
|
|
|
|
aFlavor.MimeType = "text/plain;charset=utf-16";
|
|
|
|
aFlavor.DataType = cppu::UnoType<OUString>::get();
|
|
|
|
|
|
|
|
Any aValue;
|
|
|
|
try
|
|
|
|
{
|
|
|
|
aValue = xTrans->getTransferData(aFlavor);
|
|
|
|
}
|
|
|
|
catch (...)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
|
|
|
if (aValue.getValueTypeClass() == TypeClass_STRING)
|
|
|
|
{
|
|
|
|
OUString aString;
|
|
|
|
aValue >>= aString;
|
|
|
|
pMimeData->setText(toQString(aString));
|
2018-10-10 13:33:15 +02:00
|
|
|
}
|
|
|
|
}
|
2019-01-17 15:31:51 +03:00
|
|
|
|
|
|
|
QClipboard* clipboard = QApplication::clipboard();
|
|
|
|
clipboard->setMimeData(pMimeData.release());
|
2018-10-02 11:36:39 +02:00
|
|
|
}
|
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
aEv.Contents = getContents();
|
|
|
|
|
|
|
|
aGuard.clear();
|
|
|
|
|
|
|
|
if (xOldOwner.is() && xOldOwner != xClipboardOwner)
|
|
|
|
xOldOwner->lostOwnership(this, xOldContents);
|
|
|
|
for (auto const& listener : aListeners)
|
|
|
|
{
|
|
|
|
listener->changedContents(aEv);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
OUString VclQt5Clipboard::getName() { return OUString("CLIPBOARD"); }
|
|
|
|
|
|
|
|
sal_Int8 VclQt5Clipboard::getRenderingCapabilities() { return 0; }
|
|
|
|
|
|
|
|
void VclQt5Clipboard::addClipboardListener(
|
|
|
|
const Reference<datatransfer::clipboard::XClipboardListener>& listener)
|
|
|
|
{
|
|
|
|
osl::ClearableMutexGuard aGuard(m_aMutex);
|
|
|
|
|
|
|
|
m_aListeners.push_back(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
void VclQt5Clipboard::removeClipboardListener(
|
|
|
|
const Reference<datatransfer::clipboard::XClipboardListener>& listener)
|
|
|
|
{
|
|
|
|
osl::ClearableMutexGuard aGuard(m_aMutex);
|
|
|
|
|
|
|
|
m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), listener),
|
|
|
|
m_aListeners.end());
|
|
|
|
}
|
|
|
|
|
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|