/* -*- 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 #include #include #include #include #include #include #include #include #include #include namespace { std::map g_nameToClipboardMap = { { "CLIPBOARD", QClipboard::Clipboard }, { "PRIMARY", QClipboard::Selection } }; QClipboard::Mode getClipboardTypeFromName(const OUString& aString) { // use QClipboard::Clipboard as fallback if requested type isn't found QClipboard::Mode aMode = QClipboard::Clipboard; auto iter = g_nameToClipboardMap.find(aString); if (iter != g_nameToClipboardMap.end()) { aMode = iter->second; } else { SAL_WARN("vcl.qt5", "Unrecognized clipboard type \"" << aString << "\" is requested, falling back to QClipboard::Clipboard"); } return aMode; } void lcl_peekFormats(const css::uno::Sequence& 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; } } } Qt5Transferable::Qt5Transferable(QClipboard::Mode aMode) : m_aClipboardMode(aMode) { } std::vector Qt5Transferable::getTransferDataFlavorsAsVector() { std::vector aVector; const QClipboard* clipboard = QApplication::clipboard(); const QMimeData* mimeData = clipboard->mimeData(m_aClipboardMode); css::datatransfer::DataFlavor aFlavor; if (mimeData) { 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::get(); aVector.push_back(aFlavor); } else { aFlavor.MimeType = toOUString(rMimeType); aFlavor.DataType = cppu::UnoType>::get(); aVector.push_back(aFlavor); } } } return aVector; } css::uno::Sequence SAL_CALL Qt5Transferable::getTransferDataFlavors() { return comphelper::containerToSequence(getTransferDataFlavorsAsVector()); } sal_Bool SAL_CALL Qt5Transferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) { const std::vector aAll = getTransferDataFlavorsAsVector(); return std::any_of(aAll.begin(), aAll.end(), [&](const css::datatransfer::DataFlavor& aFlavor) { return rFlavor.MimeType == aFlavor.MimeType; }); //FIXME } /* * XTransferable */ css::uno::Any SAL_CALL Qt5Transferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor) { css::uno::Any aRet; const QClipboard* clipboard = QApplication::clipboard(); const QMimeData* mimeData = clipboard->mimeData(m_aClipboardMode); if (mimeData) { if (rFlavor.MimeType == "text/plain;charset=utf-16") { QString clipboardContent = mimeData->text(); OUString sContent = toOUString(clipboardContent); aRet <<= sContent.replaceAll("\r\n", "\n"); } else if (rFlavor.MimeType == "text/html") { QString clipboardContent = mimeData->html(); std::string aStr = clipboardContent.toStdString(); Sequence aSeq(reinterpret_cast(aStr.c_str()), aStr.length()); aRet <<= aSeq; } else if (rFlavor.MimeType.startsWith("image") && mimeData->hasImage()) { QImage image = qvariant_cast(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 aSeq(reinterpret_cast(ba.data()), ba.size()); aRet <<= aSeq; } } return aRet; } VclQt5Clipboard::VclQt5Clipboard(const OUString& aModeString) : cppu::WeakComponentImplHelper( m_aMutex) , m_aClipboardName(aModeString) , m_aClipboardMode(getClipboardTypeFromName(aModeString)) { } void VclQt5Clipboard::flushClipboard() { SolarMutexGuard aGuard; return; } VclQt5Clipboard::~VclQt5Clipboard() {} OUString VclQt5Clipboard::getImplementationName() { return OUString("com.sun.star.datatransfer.VclQt5Clipboard"); } Sequence VclQt5Clipboard::getSupportedServiceNames() { Sequence aRet{ "com.sun.star.datatransfer.clipboard.SystemClipboard" }; return aRet; } sal_Bool VclQt5Clipboard::supportsService(const OUString& ServiceName) { return cppu::supportsService(this, ServiceName); } Reference VclQt5Clipboard::getContents() { if (!m_aContents.is()) m_aContents = new Qt5Transferable(m_aClipboardMode); return m_aContents; } void VclQt5Clipboard::setContents( const Reference& xTrans, const Reference& xClipboardOwner) { osl::ClearableMutexGuard aGuard(m_aMutex); Reference xOldOwner(m_aOwner); Reference xOldContents(m_aContents); m_aContents = xTrans; m_aOwner = xClipboardOwner; std::vector> aListeners(m_aListeners); datatransfer::clipboard::ClipboardEvent aEv; QClipboard* clipboard = QApplication::clipboard(); switch (m_aClipboardMode) { case QClipboard::Selection: if (!clipboard->supportsSelection()) { return; } break; case QClipboard::FindBuffer: if (!clipboard->supportsFindBuffer()) { return; } break; case QClipboard::Clipboard: default: break; } if (m_aContents.is()) { css::uno::Sequence aFormats = xTrans->getTransferDataFlavors(); bool bHasHtml = false, bHasImage = false; lcl_peekFormats(aFormats, bHasHtml, bHasImage); std::unique_ptr pMimeData(new QMimeData); // Add html data if present if (bHasHtml) { css::datatransfer::DataFlavor aFlavor; aFlavor.MimeType = "text/html"; aFlavor.DataType = cppu::UnoType>::get(); Any aValue; try { aValue = xTrans->getTransferData(aFlavor); } catch (...) { } if (aValue.getValueType() == cppu::UnoType>::get()) { Sequence aData; aValue >>= aData; OUString aHtmlAsString(reinterpret_cast(aData.getConstArray()), aData.getLength(), RTL_TEXTENCODING_UTF8); pMimeData->setHtml(toQString(aHtmlAsString)); } } // Add image data if present if (bHasImage) { css::datatransfer::DataFlavor aFlavor; //FIXME: other image formats? aFlavor.MimeType = "image/png"; aFlavor.DataType = cppu::UnoType>::get(); Any aValue; try { aValue = xTrans->getTransferData(aFlavor); } catch (...) { } if (aValue.getValueType() == cppu::UnoType>::get()) { Sequence aData; aValue >>= aData; QImage image; image.loadFromData(reinterpret_cast(aData.getConstArray()), aData.getLength()); 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::get(); Any aValue; try { aValue = xTrans->getTransferData(aFlavor); } catch (...) { } if (aValue.getValueTypeClass() == TypeClass_STRING) { OUString aString; aValue >>= aString; pMimeData->setText(toQString(aString)); } } clipboard->setMimeData(pMimeData.release(), m_aClipboardMode); } 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 m_aClipboardName; } sal_Int8 VclQt5Clipboard::getRenderingCapabilities() { return 0; } void VclQt5Clipboard::addClipboardListener( const Reference& listener) { osl::ClearableMutexGuard aGuard(m_aMutex); m_aListeners.push_back(listener); } void VclQt5Clipboard::removeClipboardListener( const Reference& 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: */