blob: fd0ed8aed2d03b614651021b4029b75b03731619 [file] [log] [blame]
/****************************************************************************
**
** Copyright (C) 2016 The Qt Company Ltd.
** Contact: https://www.qt.io/licensing/
**
** This file is part of the plugins of the Qt Toolkit.
**
** $QT_BEGIN_LICENSE:LGPL$
** Commercial License Usage
** Licensees holding valid commercial Qt licenses may use this file in
** accordance with the commercial license agreement provided with the
** Software or, alternatively, in accordance with the terms contained in
** a written agreement between you and The Qt Company. For licensing terms
** and conditions see https://www.qt.io/terms-conditions. For further
** information use the contact form at https://www.qt.io/contact-us.
**
** GNU Lesser General Public License Usage
** Alternatively, this file may be used under the terms of the GNU Lesser
** General Public License version 3 as published by the Free Software
** Foundation and appearing in the file LICENSE.LGPL3 included in the
** packaging of this file. Please review the following information to
** ensure the GNU Lesser General Public License version 3 requirements
** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
**
** GNU General Public License Usage
** Alternatively, this file may be used under the terms of the GNU
** General Public License version 2.0 or (at your option) the GNU General
** Public license version 3 or any later version approved by the KDE Free
** Qt Foundation. The licenses are as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
** included in the packaging of this file. Please review the following
** information to ensure the GNU General Public License requirements will
** be met: https://www.gnu.org/licenses/gpl-2.0.html and
** https://www.gnu.org/licenses/gpl-3.0.html.
**
** $QT_END_LICENSE$
**
****************************************************************************/
#include "qwinrtclipboard.h"
#include <QtCore/QCoreApplication>
#include <QtCore/qfunctions_winrt.h>
#include <QtCore/private/qeventdispatcher_winrt_p.h>
#include <Windows.ApplicationModel.datatransfer.h>
#include <functional>
using namespace ABI::Windows::ApplicationModel::DataTransfer;
using namespace ABI::Windows::Foundation;
using namespace Microsoft::WRL;
using namespace Microsoft::WRL::Wrappers;
typedef IEventHandler<IInspectable *> ContentChangedHandler;
#define RETURN_NULLPTR_IF_FAILED(msg) RETURN_IF_FAILED(msg, return nullptr)
QT_BEGIN_NAMESPACE
QWinRTClipboard::QWinRTClipboard()
: m_mimeData(nullptr)
{
QEventDispatcherWinRT::runOnXamlThread([this]() {
HRESULT hr;
hr = GetActivationFactory(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_Clipboard).Get(),
&m_nativeClipBoard);
Q_ASSERT_SUCCEEDED(hr);
EventRegistrationToken tok;
hr = m_nativeClipBoard->add_ContentChanged(Callback<ContentChangedHandler>(this, &QWinRTClipboard::onContentChanged).Get(), &tok);
Q_ASSERT_SUCCEEDED(hr);
return hr;
});
}
QMimeData *QWinRTClipboard::mimeData(QClipboard::Mode mode)
{
if (!supportsMode(mode))
return nullptr;
ComPtr<IDataPackageView> view;
HRESULT hr;
hr = m_nativeClipBoard->GetContent(&view);
RETURN_NULLPTR_IF_FAILED("Could not get clipboard content.");
ComPtr<IAsyncOperation<HSTRING>> op;
HString result;
// This throws a security exception (WinRT originate error / 0x40080201.
// Unfortunately there seems to be no way to avoid this, neither
// running on the XAML thread, nor some other way. Stack Overflow
// confirms this problem since Windows (Phone) 8.0.
hr = view->GetTextAsync(&op);
RETURN_NULLPTR_IF_FAILED("Could not get clipboard text.");
hr = QWinRTFunctions::await(op, result.GetAddressOf());
RETURN_NULLPTR_IF_FAILED("Could not get clipboard text content");
quint32 size;
const wchar_t *textStr = result.GetRawBuffer(&size);
QString text = QString::fromWCharArray(textStr, int(size));
text.replace(QLatin1String("\r\n"), QLatin1String("\n"));
if (m_mimeData) {
if (m_mimeData->text() == text)
return m_mimeData;
delete m_mimeData;
}
m_mimeData = new QMimeData();
m_mimeData->setText(text);
return m_mimeData;
}
// Inspired by QWindowsMimeText::convertFromMime
inline QString convertToWindowsLineEnding(const QString &text)
{
const QChar *u = text.unicode();
QString res;
const int s = text.length();
int maxsize = s + s / 40 + 3;
res.resize(maxsize);
int ri = 0;
bool cr = false;
for (int i = 0; i < s; ++i) {
if (*u == QLatin1Char('\r'))
cr = true;
else {
if (*u == QLatin1Char('\n') && !cr)
res[ri++] = QLatin1Char('\r');
cr = false;
}
res[ri++] = *u;
if (ri+3 >= maxsize) {
maxsize += maxsize / 4;
res.resize(maxsize);
}
++u;
}
res.truncate(ri);
return res;
}
void QWinRTClipboard::setMimeData(QMimeData *data, QClipboard::Mode mode)
{
if (!supportsMode(mode))
return;
const bool newData = !m_mimeData || m_mimeData != data;
if (newData) {
if (m_mimeData)
delete m_mimeData;
m_mimeData = data;
}
const QString text = data ? data->text() : QString();
HRESULT hr = QEventDispatcherWinRT::runOnXamlThread([this, text]() {
HRESULT hr;
ComPtr<IDataPackage> package;
hr = RoActivateInstance(HString::MakeReference(RuntimeClass_Windows_ApplicationModel_DataTransfer_DataPackage).Get(),
&package);
const QString nativeString = convertToWindowsLineEnding(text);
HStringReference textRef(reinterpret_cast<LPCWSTR>(nativeString.utf16()),
uint(nativeString.length()));
hr = package->SetText(textRef.Get());
RETURN_HR_IF_FAILED("Could not set text to clipboard data package.");
hr = m_nativeClipBoard->SetContent(package.Get());
RETURN_HR_IF_FAILED("Could not set clipboard content.");
return S_OK;
});
RETURN_VOID_IF_FAILED("Could not set clipboard text.");
}
bool QWinRTClipboard::supportsMode(QClipboard::Mode mode) const
{
return mode == QClipboard::Clipboard;
}
HRESULT QWinRTClipboard::onContentChanged(IInspectable *, IInspectable *)
{
emitChanged(QClipboard::Clipboard);
return S_OK;
}
QT_END_NAMESPACE