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/.
|
|
|
|
*
|
|
|
|
*/
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
#include <Qt5Clipboard.hxx>
|
|
|
|
#include <Qt5Clipboard.moc>
|
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
#include <cppuhelper/supportsservice.hxx>
|
2019-01-18 10:59:50 +03:00
|
|
|
#include <sal/log.hxx>
|
2018-09-14 14:53:40 +02:00
|
|
|
|
2019-06-01 03:36:36 +00:00
|
|
|
#include <QtWidgets/QApplication>
|
2018-09-18 12:23:34 +02:00
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
#include <Qt5Instance.hxx>
|
2019-06-01 03:36:36 +00:00
|
|
|
#include <Qt5Transferable.hxx>
|
2018-09-18 12:23:34 +02:00
|
|
|
#include <Qt5Tools.hxx>
|
2019-01-18 10:59:50 +03:00
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
#include <cassert>
|
2019-01-18 10:59:50 +03:00
|
|
|
#include <map>
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
Qt5Clipboard::Qt5Clipboard(const OUString& aModeString, const QClipboard::Mode aMode)
|
|
|
|
: cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard,
|
|
|
|
css::datatransfer::clipboard::XFlushableClipboard,
|
|
|
|
XServiceInfo>(m_aMutex)
|
|
|
|
, m_aClipboardName(aModeString)
|
|
|
|
, m_aClipboardMode(aMode)
|
2019-10-02 13:31:46 +00:00
|
|
|
, m_bOwnClipboardChange(false)
|
2019-01-18 10:59:50 +03:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
assert(isSupported(m_aClipboardMode));
|
2019-06-14 06:32:16 +00:00
|
|
|
// DirectConnection guarantees the changed slot runs in the same thread as the QClipboard
|
2019-05-31 16:40:34 +00:00
|
|
|
connect(QApplication::clipboard(), &QClipboard::changed, this, &Qt5Clipboard::handleChanged,
|
|
|
|
Qt::DirectConnection);
|
2019-01-18 10:59:50 +03:00
|
|
|
}
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
css::uno::Reference<css::uno::XInterface> Qt5Clipboard::create(const OUString& aModeString)
|
2018-10-10 13:33:15 +02:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
static const std::map<OUString, QClipboard::Mode> aNameToClipboardMap
|
|
|
|
= { { "CLIPBOARD", QClipboard::Clipboard }, { "PRIMARY", QClipboard::Selection } };
|
2018-10-10 13:33:15 +02:00
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
assert(QApplication::clipboard()->thread() == qApp->thread());
|
2018-09-14 14:53:40 +02:00
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
auto iter = aNameToClipboardMap.find(aModeString);
|
|
|
|
if (iter != aNameToClipboardMap.end() && isSupported(iter->second))
|
|
|
|
return static_cast<cppu::OWeakObject*>(new Qt5Clipboard(aModeString, iter->second));
|
|
|
|
SAL_WARN("vcl.qt5", "Ignoring unrecognized clipboard type: '" << aModeString << "'");
|
|
|
|
return css::uno::Reference<css::uno::XInterface>();
|
2018-09-14 14:53:40 +02:00
|
|
|
}
|
|
|
|
|
2019-05-31 16:34:37 +00:00
|
|
|
void Qt5Clipboard::flushClipboard()
|
2018-09-14 14:53:40 +02:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
|
|
|
|
SolarMutexGuard g;
|
|
|
|
pSalInst->RunInMainThread([&, this]() {
|
|
|
|
if (!isOwner(m_aClipboardMode))
|
|
|
|
return;
|
|
|
|
|
|
|
|
QClipboard* pClipboard = QApplication::clipboard();
|
|
|
|
const Qt5MimeData* pQt5MimeData
|
|
|
|
= dynamic_cast<const Qt5MimeData*>(pClipboard->mimeData(m_aClipboardMode));
|
|
|
|
assert(pQt5MimeData);
|
|
|
|
|
|
|
|
QMimeData* pMimeCopy = nullptr;
|
|
|
|
if (pQt5MimeData && pQt5MimeData->deepCopy(&pMimeCopy))
|
2019-10-02 13:31:46 +00:00
|
|
|
{
|
|
|
|
m_bOwnClipboardChange = true;
|
2019-05-31 16:40:34 +00:00
|
|
|
pClipboard->setMimeData(pMimeCopy, m_aClipboardMode);
|
2019-10-02 13:31:46 +00:00
|
|
|
m_bOwnClipboardChange = false;
|
|
|
|
}
|
2019-05-31 16:40:34 +00:00
|
|
|
});
|
2018-09-14 14:53:40 +02:00
|
|
|
}
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
css::uno::Reference<css::datatransfer::XTransferable> Qt5Clipboard::getContents()
|
2018-09-14 14:53:40 +02:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
osl::MutexGuard aGuard(m_aMutex);
|
|
|
|
|
2019-10-01 20:02:25 +02:00
|
|
|
// if we're the owner, we might have the XTransferable from setContents. but
|
|
|
|
// maybe a non-LO clipboard change from within LO, like some C'n'P in the
|
|
|
|
// QFileDialog, might have invalidated m_aContents, so we need to check it too.
|
|
|
|
if (isOwner(m_aClipboardMode) && m_aContents.is())
|
2019-05-31 16:40:34 +00:00
|
|
|
return m_aContents;
|
|
|
|
|
|
|
|
// check if we can still use the shared Qt5ClipboardTransferable
|
|
|
|
const QMimeData* pMimeData = QApplication::clipboard()->mimeData(m_aClipboardMode);
|
|
|
|
if (m_aContents.is())
|
|
|
|
{
|
|
|
|
const auto* pTrans = dynamic_cast<Qt5ClipboardTransferable*>(m_aContents.get());
|
|
|
|
assert(pTrans);
|
|
|
|
if (pTrans && pTrans->mimeData() == pMimeData)
|
|
|
|
return m_aContents;
|
|
|
|
}
|
|
|
|
|
|
|
|
m_aContents = new Qt5ClipboardTransferable(m_aClipboardMode, pMimeData);
|
2018-09-14 14:53:40 +02:00
|
|
|
return m_aContents;
|
|
|
|
}
|
|
|
|
|
2019-05-31 16:34:37 +00:00
|
|
|
void Qt5Clipboard::setContents(
|
2019-05-31 16:40:34 +00:00
|
|
|
const css::uno::Reference<css::datatransfer::XTransferable>& xTrans,
|
|
|
|
const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
|
2018-09-14 14:53:40 +02:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
// it's actually possible to get a non-empty xTrans and an empty xClipboardOwner!
|
2018-09-14 14:53:40 +02:00
|
|
|
osl::ClearableMutexGuard aGuard(m_aMutex);
|
2019-05-31 16:40:34 +00:00
|
|
|
|
|
|
|
css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner);
|
|
|
|
css::uno::Reference<css::datatransfer::XTransferable> xOldContents(m_aContents);
|
2018-09-14 14:53:40 +02:00
|
|
|
m_aContents = xTrans;
|
|
|
|
m_aOwner = xClipboardOwner;
|
|
|
|
|
2019-10-02 13:31:46 +00:00
|
|
|
m_bOwnClipboardChange = true;
|
2018-10-02 11:36:39 +02:00
|
|
|
if (m_aContents.is())
|
2019-05-31 16:40:34 +00:00
|
|
|
QApplication::clipboard()->setMimeData(new Qt5MimeData(m_aContents), m_aClipboardMode);
|
|
|
|
else
|
2018-10-02 11:36:39 +02:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
assert(!m_aOwner.is());
|
|
|
|
QApplication::clipboard()->clear(m_aClipboardMode);
|
2018-10-02 11:36:39 +02:00
|
|
|
}
|
2019-10-02 13:31:46 +00:00
|
|
|
m_bOwnClipboardChange = false;
|
2018-10-02 11:36:39 +02:00
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
aGuard.clear();
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
// we have to notify only an owner change, since handleChanged can't
|
|
|
|
// access the previous owner anymore and can just handle lost ownership.
|
2018-09-14 14:53:40 +02:00
|
|
|
if (xOldOwner.is() && xOldOwner != xClipboardOwner)
|
|
|
|
xOldOwner->lostOwnership(this, xOldContents);
|
|
|
|
}
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
void Qt5Clipboard::handleChanged(QClipboard::Mode aMode)
|
tdf#122689 qt5: Consider external clipboard updates
Previously, once 'm_aContents' had been assigned in
'VclQt5Clipboard::setContents()', its value (or the one set
in a subsequent call to the same method) was always
returned in 'VclQt5Clipboard::getContents()', thus
ignoring all system clipboard updates done by any other
third-party applications, preventing copy-paste from other
applications.
In order to take external clipboard updates into account,
add a slot for the 'QClipboard::changed' signal and
drop the current own clipboard content if the clipboard
has been updated by another application.
In order to detect whether the clipboard update was made
by this 'VclQt5Clipboard' itself or elsewhere, a custom MIME type
"application/x-libreoffice-clipboard-uuid" is added, whose
value is set to the clipboard's (randomly generated) UUID.
If the entry is present and has the correct value, the clipboard
content was added by this clipboard and can be kept.
Otherwise, clear 'm_aContents', so that it's newly assigned
in 'VclQt5Clipboard::getContents()', taking into account
the external clipboard update.
[Side note: Testing showed that, on Wayland, more
'QClipboard::changed' events were emitted without the clipboard
content actually having changed (e.g. when switching focus between
windows), which is why an approach of simply setting a flag and
checking for that one is not enough, like "wrapping" the
'QClipboard::setMimeData()' call in 'VclQt5Clipboard::setContents()'
as follows
m_bIsFillingClipboard = true;
clipboard->setMimeData(pMimeData.release(), m_aClipboardMode);
m_bIsFillingClipboard = false;
and then evaluating the 'm_bIsfillingClipboard' flag in
'VclQt5Clipboard::handleClipboardChange' instead of using the UUID-based
approach. These additional 'QClipboard::changed' events did not
show up the same way while testing on X11.]
Change-Id: Ib3a6a4f9b7f5ca3573666fb9c072ae97cf2e0049
Reviewed-on: https://gerrit.libreoffice.org/68214
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
2019-02-22 14:12:35 +01:00
|
|
|
{
|
2019-05-31 16:40:34 +00:00
|
|
|
if (aMode != m_aClipboardMode)
|
|
|
|
return;
|
|
|
|
|
|
|
|
osl::ClearableMutexGuard aGuard(m_aMutex);
|
|
|
|
|
|
|
|
css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> xOldOwner(m_aOwner);
|
|
|
|
css::uno::Reference<css::datatransfer::XTransferable> xOldContents(m_aContents);
|
|
|
|
// ownership change from LO POV is handled in setContents
|
2019-10-02 13:31:46 +00:00
|
|
|
if (!m_bOwnClipboardChange)
|
tdf#122689 qt5: Consider external clipboard updates
Previously, once 'm_aContents' had been assigned in
'VclQt5Clipboard::setContents()', its value (or the one set
in a subsequent call to the same method) was always
returned in 'VclQt5Clipboard::getContents()', thus
ignoring all system clipboard updates done by any other
third-party applications, preventing copy-paste from other
applications.
In order to take external clipboard updates into account,
add a slot for the 'QClipboard::changed' signal and
drop the current own clipboard content if the clipboard
has been updated by another application.
In order to detect whether the clipboard update was made
by this 'VclQt5Clipboard' itself or elsewhere, a custom MIME type
"application/x-libreoffice-clipboard-uuid" is added, whose
value is set to the clipboard's (randomly generated) UUID.
If the entry is present and has the correct value, the clipboard
content was added by this clipboard and can be kept.
Otherwise, clear 'm_aContents', so that it's newly assigned
in 'VclQt5Clipboard::getContents()', taking into account
the external clipboard update.
[Side note: Testing showed that, on Wayland, more
'QClipboard::changed' events were emitted without the clipboard
content actually having changed (e.g. when switching focus between
windows), which is why an approach of simply setting a flag and
checking for that one is not enough, like "wrapping" the
'QClipboard::setMimeData()' call in 'VclQt5Clipboard::setContents()'
as follows
m_bIsFillingClipboard = true;
clipboard->setMimeData(pMimeData.release(), m_aClipboardMode);
m_bIsFillingClipboard = false;
and then evaluating the 'm_bIsfillingClipboard' flag in
'VclQt5Clipboard::handleClipboardChange' instead of using the UUID-based
approach. These additional 'QClipboard::changed' events did not
show up the same way while testing on X11.]
Change-Id: Ib3a6a4f9b7f5ca3573666fb9c072ae97cf2e0049
Reviewed-on: https://gerrit.libreoffice.org/68214
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
2019-02-22 14:12:35 +01:00
|
|
|
{
|
|
|
|
m_aContents.clear();
|
2019-05-31 16:40:34 +00:00
|
|
|
m_aOwner.clear();
|
|
|
|
}
|
|
|
|
|
|
|
|
std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> aListeners(
|
|
|
|
m_aListeners);
|
|
|
|
css::datatransfer::clipboard::ClipboardEvent aEv;
|
|
|
|
aEv.Contents = getContents();
|
|
|
|
|
|
|
|
aGuard.clear();
|
|
|
|
|
2019-10-02 13:31:46 +00:00
|
|
|
if (!m_bOwnClipboardChange && xOldOwner.is())
|
2019-05-31 16:40:34 +00:00
|
|
|
xOldOwner->lostOwnership(this, xOldContents);
|
|
|
|
for (auto const& listener : aListeners)
|
|
|
|
listener->changedContents(aEv);
|
|
|
|
}
|
|
|
|
|
2019-07-30 17:45:37 +02:00
|
|
|
OUString Qt5Clipboard::getImplementationName() { return "com.sun.star.datatransfer.Qt5Clipboard"; }
|
2019-06-09 14:43:59 +00:00
|
|
|
|
|
|
|
css::uno::Sequence<OUString> Qt5Clipboard::getSupportedServiceNames()
|
|
|
|
{
|
|
|
|
return { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
|
|
|
|
}
|
|
|
|
|
|
|
|
sal_Bool Qt5Clipboard::supportsService(const OUString& ServiceName)
|
|
|
|
{
|
|
|
|
return cppu::supportsService(this, ServiceName);
|
|
|
|
}
|
|
|
|
|
|
|
|
OUString Qt5Clipboard::getName() { return m_aClipboardName; }
|
|
|
|
|
|
|
|
sal_Int8 Qt5Clipboard::getRenderingCapabilities() { return 0; }
|
|
|
|
|
|
|
|
void Qt5Clipboard::addClipboardListener(
|
|
|
|
const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
|
|
|
|
{
|
|
|
|
osl::MutexGuard aGuard(m_aMutex);
|
|
|
|
m_aListeners.push_back(listener);
|
|
|
|
}
|
|
|
|
|
|
|
|
void Qt5Clipboard::removeClipboardListener(
|
|
|
|
const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
|
|
|
|
{
|
|
|
|
osl::MutexGuard aGuard(m_aMutex);
|
|
|
|
m_aListeners.erase(std::remove(m_aListeners.begin(), m_aListeners.end(), listener),
|
|
|
|
m_aListeners.end());
|
|
|
|
}
|
|
|
|
|
2019-05-31 16:40:34 +00:00
|
|
|
bool Qt5Clipboard::isSupported(const QClipboard::Mode aMode)
|
|
|
|
{
|
|
|
|
const QClipboard* pClipboard = QApplication::clipboard();
|
|
|
|
switch (aMode)
|
|
|
|
{
|
|
|
|
case QClipboard::Selection:
|
|
|
|
return pClipboard->supportsSelection();
|
|
|
|
|
|
|
|
case QClipboard::FindBuffer:
|
|
|
|
return pClipboard->supportsFindBuffer();
|
|
|
|
|
|
|
|
case QClipboard::Clipboard:
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
bool Qt5Clipboard::isOwner(const QClipboard::Mode aMode)
|
|
|
|
{
|
|
|
|
if (!isSupported(aMode))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
const QClipboard* pClipboard = QApplication::clipboard();
|
|
|
|
switch (aMode)
|
|
|
|
{
|
|
|
|
case QClipboard::Selection:
|
|
|
|
return pClipboard->ownsSelection();
|
|
|
|
|
|
|
|
case QClipboard::FindBuffer:
|
|
|
|
return pClipboard->ownsFindBuffer();
|
|
|
|
|
|
|
|
case QClipboard::Clipboard:
|
|
|
|
return pClipboard->ownsClipboard();
|
tdf#122689 qt5: Consider external clipboard updates
Previously, once 'm_aContents' had been assigned in
'VclQt5Clipboard::setContents()', its value (or the one set
in a subsequent call to the same method) was always
returned in 'VclQt5Clipboard::getContents()', thus
ignoring all system clipboard updates done by any other
third-party applications, preventing copy-paste from other
applications.
In order to take external clipboard updates into account,
add a slot for the 'QClipboard::changed' signal and
drop the current own clipboard content if the clipboard
has been updated by another application.
In order to detect whether the clipboard update was made
by this 'VclQt5Clipboard' itself or elsewhere, a custom MIME type
"application/x-libreoffice-clipboard-uuid" is added, whose
value is set to the clipboard's (randomly generated) UUID.
If the entry is present and has the correct value, the clipboard
content was added by this clipboard and can be kept.
Otherwise, clear 'm_aContents', so that it's newly assigned
in 'VclQt5Clipboard::getContents()', taking into account
the external clipboard update.
[Side note: Testing showed that, on Wayland, more
'QClipboard::changed' events were emitted without the clipboard
content actually having changed (e.g. when switching focus between
windows), which is why an approach of simply setting a flag and
checking for that one is not enough, like "wrapping" the
'QClipboard::setMimeData()' call in 'VclQt5Clipboard::setContents()'
as follows
m_bIsFillingClipboard = true;
clipboard->setMimeData(pMimeData.release(), m_aClipboardMode);
m_bIsFillingClipboard = false;
and then evaluating the 'm_bIsfillingClipboard' flag in
'VclQt5Clipboard::handleClipboardChange' instead of using the UUID-based
approach. These additional 'QClipboard::changed' events did not
show up the same way while testing on X11.]
Change-Id: Ib3a6a4f9b7f5ca3573666fb9c072ae97cf2e0049
Reviewed-on: https://gerrit.libreoffice.org/68214
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
2019-02-22 14:12:35 +01:00
|
|
|
}
|
2019-05-31 16:40:34 +00:00
|
|
|
return false;
|
tdf#122689 qt5: Consider external clipboard updates
Previously, once 'm_aContents' had been assigned in
'VclQt5Clipboard::setContents()', its value (or the one set
in a subsequent call to the same method) was always
returned in 'VclQt5Clipboard::getContents()', thus
ignoring all system clipboard updates done by any other
third-party applications, preventing copy-paste from other
applications.
In order to take external clipboard updates into account,
add a slot for the 'QClipboard::changed' signal and
drop the current own clipboard content if the clipboard
has been updated by another application.
In order to detect whether the clipboard update was made
by this 'VclQt5Clipboard' itself or elsewhere, a custom MIME type
"application/x-libreoffice-clipboard-uuid" is added, whose
value is set to the clipboard's (randomly generated) UUID.
If the entry is present and has the correct value, the clipboard
content was added by this clipboard and can be kept.
Otherwise, clear 'm_aContents', so that it's newly assigned
in 'VclQt5Clipboard::getContents()', taking into account
the external clipboard update.
[Side note: Testing showed that, on Wayland, more
'QClipboard::changed' events were emitted without the clipboard
content actually having changed (e.g. when switching focus between
windows), which is why an approach of simply setting a flag and
checking for that one is not enough, like "wrapping" the
'QClipboard::setMimeData()' call in 'VclQt5Clipboard::setContents()'
as follows
m_bIsFillingClipboard = true;
clipboard->setMimeData(pMimeData.release(), m_aClipboardMode);
m_bIsFillingClipboard = false;
and then evaluating the 'm_bIsfillingClipboard' flag in
'VclQt5Clipboard::handleClipboardChange' instead of using the UUID-based
approach. These additional 'QClipboard::changed' events did not
show up the same way while testing on X11.]
Change-Id: Ib3a6a4f9b7f5ca3573666fb9c072ae97cf2e0049
Reviewed-on: https://gerrit.libreoffice.org/68214
Tested-by: Jenkins
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
2019-02-22 14:12:35 +01:00
|
|
|
}
|
|
|
|
|
2018-09-14 14:53:40 +02:00
|
|
|
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|