1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
11 #include <Qt5Clipboard.hxx>
12 #include <Qt5Clipboard.moc>
14 #include <cppuhelper/supportsservice.hxx>
15 #include <sal/log.hxx>
17 #include <QtWidgets/QApplication>
19 #include <Qt5Instance.hxx>
20 #include <Qt5Transferable.hxx>
21 #include <Qt5Tools.hxx>
26 Qt5Clipboard::Qt5Clipboard(const OUString
& aModeString
, const QClipboard::Mode aMode
)
27 : cppu::WeakComponentImplHelper
<css::datatransfer::clipboard::XSystemClipboard
,
28 css::datatransfer::clipboard::XFlushableClipboard
,
29 XServiceInfo
>(m_aMutex
)
30 , m_aClipboardName(aModeString
)
31 , m_aClipboardMode(aMode
)
32 , m_bOwnClipboardChange(false)
35 assert(isSupported(m_aClipboardMode
));
36 // DirectConnection guarantees the changed slot runs in the same thread as the QClipboard
37 connect(QApplication::clipboard(), &QClipboard::changed
, this, &Qt5Clipboard::handleChanged
,
38 Qt::DirectConnection
);
40 // explicitly queue an event, so we can eventually ignore it
41 connect(this, &Qt5Clipboard::clearClipboard
, this, &Qt5Clipboard::handleClearClipboard
,
42 Qt::QueuedConnection
);
45 css::uno::Reference
<css::uno::XInterface
> Qt5Clipboard::create(const OUString
& aModeString
)
47 static const std::map
<OUString
, QClipboard::Mode
> aNameToClipboardMap
48 = { { "CLIPBOARD", QClipboard::Clipboard
}, { "PRIMARY", QClipboard::Selection
} };
50 assert(QApplication::clipboard()->thread() == qApp
->thread());
52 auto iter
= aNameToClipboardMap
.find(aModeString
);
53 if (iter
!= aNameToClipboardMap
.end() && isSupported(iter
->second
))
54 return static_cast<cppu::OWeakObject
*>(new Qt5Clipboard(aModeString
, iter
->second
));
55 SAL_WARN("vcl.qt5", "Ignoring unrecognized clipboard type: '" << aModeString
<< "'");
56 return css::uno::Reference
<css::uno::XInterface
>();
59 void Qt5Clipboard::flushClipboard()
61 auto* pSalInst(static_cast<Qt5Instance
*>(GetSalData()->m_pInstance
));
63 pSalInst
->RunInMainThread([&, this]() {
64 if (!isOwner(m_aClipboardMode
))
67 QClipboard
* pClipboard
= QApplication::clipboard();
68 const Qt5MimeData
* pQt5MimeData
69 = dynamic_cast<const Qt5MimeData
*>(pClipboard
->mimeData(m_aClipboardMode
));
72 QMimeData
* pMimeCopy
= nullptr;
73 if (pQt5MimeData
&& pQt5MimeData
->deepCopy(&pMimeCopy
))
75 m_bOwnClipboardChange
= true;
76 pClipboard
->setMimeData(pMimeCopy
, m_aClipboardMode
);
77 m_bOwnClipboardChange
= false;
82 css::uno::Reference
<css::datatransfer::XTransferable
> Qt5Clipboard::getContents()
84 osl::MutexGuard
aGuard(m_aMutex
);
86 // if we're the owner, we might have the XTransferable from setContents. but
87 // maybe a non-LO clipboard change from within LO, like some C'n'P in the
88 // QFileDialog, might have invalidated m_aContents, so we need to check it too.
89 if (isOwner(m_aClipboardMode
) && m_aContents
.is())
92 // check if we can still use the shared Qt5ClipboardTransferable
93 const QMimeData
* pMimeData
= QApplication::clipboard()->mimeData(m_aClipboardMode
);
96 const auto* pTrans
= dynamic_cast<Qt5ClipboardTransferable
*>(m_aContents
.get());
98 if (pTrans
&& pTrans
->mimeData() == pMimeData
)
102 m_aContents
= new Qt5ClipboardTransferable(m_aClipboardMode
, pMimeData
);
106 void Qt5Clipboard::handleClearClipboard()
110 QApplication::clipboard()->clear(m_aClipboardMode
);
113 void Qt5Clipboard::setContents(
114 const css::uno::Reference
<css::datatransfer::XTransferable
>& xTrans
,
115 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
>& xClipboardOwner
)
117 // it's actually possible to get a non-empty xTrans and an empty xClipboardOwner!
118 osl::ClearableMutexGuard
aGuard(m_aMutex
);
120 css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
> xOldOwner(m_aOwner
);
121 css::uno::Reference
<css::datatransfer::XTransferable
> xOldContents(m_aContents
);
122 m_aContents
= xTrans
;
123 m_aOwner
= xClipboardOwner
;
125 m_bDoClear
= !m_aContents
.is();
128 m_bOwnClipboardChange
= true;
129 QApplication::clipboard()->setMimeData(new Qt5MimeData(m_aContents
), m_aClipboardMode
);
130 m_bOwnClipboardChange
= false;
134 assert(!m_aOwner
.is());
135 Q_EMIT
clearClipboard();
140 // we have to notify only an owner change, since handleChanged can't
141 // access the previous owner anymore and can just handle lost ownership.
142 if (xOldOwner
.is() && xOldOwner
!= xClipboardOwner
)
143 xOldOwner
->lostOwnership(this, xOldContents
);
146 void Qt5Clipboard::handleChanged(QClipboard::Mode aMode
)
148 if (aMode
!= m_aClipboardMode
)
151 osl::ClearableMutexGuard
aGuard(m_aMutex
);
153 // QtWayland will send a second change notification (seemingly without any
154 // trigger). And any C'n'P operation in the Qt file picker emits a signal,
155 // with LO still holding the clipboard ownership, but internally having lost
156 // it. So ignore any signal, which still delivers the internal Qt5MimeData
157 // as the clipboard content and is no "advertised" change.
158 if (!m_bOwnClipboardChange
&& isOwner(aMode
)
159 && dynamic_cast<const Qt5MimeData
*>(QApplication::clipboard()->mimeData(aMode
)))
162 css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
> xOldOwner(m_aOwner
);
163 css::uno::Reference
<css::datatransfer::XTransferable
> xOldContents(m_aContents
);
164 // ownership change from LO POV is handled in setContents
165 if (!m_bOwnClipboardChange
)
171 std::vector
<css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>> aListeners(
173 css::datatransfer::clipboard::ClipboardEvent aEv
;
174 aEv
.Contents
= getContents();
178 if (!m_bOwnClipboardChange
&& xOldOwner
.is())
179 xOldOwner
->lostOwnership(this, xOldContents
);
180 for (auto const& listener
: aListeners
)
181 listener
->changedContents(aEv
);
184 OUString
Qt5Clipboard::getImplementationName() { return "com.sun.star.datatransfer.Qt5Clipboard"; }
186 css::uno::Sequence
<OUString
> Qt5Clipboard::getSupportedServiceNames()
188 return { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
191 sal_Bool
Qt5Clipboard::supportsService(const OUString
& ServiceName
)
193 return cppu::supportsService(this, ServiceName
);
196 OUString
Qt5Clipboard::getName() { return m_aClipboardName
; }
198 sal_Int8
Qt5Clipboard::getRenderingCapabilities() { return 0; }
200 void Qt5Clipboard::addClipboardListener(
201 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>& listener
)
203 osl::MutexGuard
aGuard(m_aMutex
);
204 m_aListeners
.push_back(listener
);
207 void Qt5Clipboard::removeClipboardListener(
208 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>& listener
)
210 osl::MutexGuard
aGuard(m_aMutex
);
211 m_aListeners
.erase(std::remove(m_aListeners
.begin(), m_aListeners
.end(), listener
),
215 bool Qt5Clipboard::isSupported(const QClipboard::Mode aMode
)
217 const QClipboard
* pClipboard
= QApplication::clipboard();
220 case QClipboard::Selection
:
221 return pClipboard
->supportsSelection();
223 case QClipboard::FindBuffer
:
224 return pClipboard
->supportsFindBuffer();
226 case QClipboard::Clipboard
:
232 bool Qt5Clipboard::isOwner(const QClipboard::Mode aMode
)
234 if (!isSupported(aMode
))
237 const QClipboard
* pClipboard
= QApplication::clipboard();
240 case QClipboard::Selection
:
241 return pClipboard
->ownsSelection();
243 case QClipboard::FindBuffer
:
244 return pClipboard
->ownsFindBuffer();
246 case QClipboard::Clipboard
:
247 return pClipboard
->ownsClipboard();
252 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */