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 <QtClipboard.hxx>
12 #include <QtClipboard.moc>
14 #include <cppuhelper/supportsservice.hxx>
15 #include <sal/log.hxx>
17 #include <QtWidgets/QApplication>
19 #include <QtInstance.hxx>
20 #include <QtTransferable.hxx>
21 #include <QtTools.hxx>
27 QtClipboard::QtClipboard(OUString aModeString
, const QClipboard::Mode aMode
)
28 : cppu::WeakComponentImplHelper
<css::datatransfer::clipboard::XSystemClipboard
,
29 css::datatransfer::clipboard::XFlushableClipboard
,
30 XServiceInfo
>(m_aMutex
)
31 , m_aClipboardName(std::move(aModeString
))
32 , m_aClipboardMode(aMode
)
33 , m_bOwnClipboardChange(false)
36 assert(isSupported(m_aClipboardMode
));
37 // DirectConnection guarantees the changed slot runs in the same thread as the QClipboard
38 connect(QApplication::clipboard(), &QClipboard::changed
, this, &QtClipboard::handleChanged
,
39 Qt::DirectConnection
);
41 // explicitly queue an event, so we can eventually ignore it
42 connect(this, &QtClipboard::clearClipboard
, this, &QtClipboard::handleClearClipboard
,
43 Qt::QueuedConnection
);
46 css::uno::Reference
<css::uno::XInterface
> QtClipboard::create(const OUString
& aModeString
)
48 static const std::map
<OUString
, QClipboard::Mode
> aNameToClipboardMap
49 = { { "CLIPBOARD", QClipboard::Clipboard
}, { "PRIMARY", QClipboard::Selection
} };
51 assert(QApplication::clipboard()->thread() == qApp
->thread());
53 auto iter
= aNameToClipboardMap
.find(aModeString
);
54 if (iter
!= aNameToClipboardMap
.end() && isSupported(iter
->second
))
55 return static_cast<cppu::OWeakObject
*>(new QtClipboard(aModeString
, iter
->second
));
56 SAL_WARN("vcl.qt", "Ignoring unrecognized clipboard type: '" << aModeString
<< "'");
57 return css::uno::Reference
<css::uno::XInterface
>();
60 void QtClipboard::flushClipboard()
62 auto* pSalInst(GetQtInstance());
64 pSalInst
->RunInMainThread([this]() {
65 if (!isOwner(m_aClipboardMode
))
68 QClipboard
* pClipboard
= QApplication::clipboard();
69 const QtMimeData
* pQtMimeData
70 = dynamic_cast<const QtMimeData
*>(pClipboard
->mimeData(m_aClipboardMode
));
73 QMimeData
* pMimeCopy
= nullptr;
74 if (pQtMimeData
&& pQtMimeData
->deepCopy(&pMimeCopy
))
76 m_bOwnClipboardChange
= true;
77 pClipboard
->setMimeData(pMimeCopy
, m_aClipboardMode
);
78 m_bOwnClipboardChange
= false;
83 css::uno::Reference
<css::datatransfer::XTransferable
> QtClipboard::getContents()
85 #if defined(EMSCRIPTEN)
86 static QMimeData aMimeData
;
88 osl::MutexGuard
aGuard(m_aMutex
);
90 // if we're the owner, we might have the XTransferable from setContents. but
91 // maybe a non-LO clipboard change from within LO, like some C'n'P in the
92 // QFileDialog, might have invalidated m_aContents, so we need to check it too.
93 if (isOwner(m_aClipboardMode
) && m_aContents
.is())
96 // check if we can still use the shared QtClipboardTransferable
97 const QMimeData
* pMimeData
= QApplication::clipboard()->mimeData(m_aClipboardMode
);
98 #if defined(EMSCRIPTEN)
100 pMimeData
= &aMimeData
;
102 if (m_aContents
.is())
104 const auto* pTrans
= dynamic_cast<QtClipboardTransferable
*>(m_aContents
.get());
106 if (pTrans
&& pTrans
->mimeData() == pMimeData
)
110 m_aContents
= new QtClipboardTransferable(m_aClipboardMode
, pMimeData
);
114 void QtClipboard::handleClearClipboard()
118 QApplication::clipboard()->clear(m_aClipboardMode
);
121 void QtClipboard::setContents(
122 const css::uno::Reference
<css::datatransfer::XTransferable
>& xTrans
,
123 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
>& xClipboardOwner
)
125 // it's actually possible to get a non-empty xTrans and an empty xClipboardOwner!
126 osl::ClearableMutexGuard
aGuard(m_aMutex
);
128 css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
> xOldOwner(m_aOwner
);
129 css::uno::Reference
<css::datatransfer::XTransferable
> xOldContents(m_aContents
);
130 m_aContents
= xTrans
;
131 m_aOwner
= xClipboardOwner
;
133 m_bDoClear
= !m_aContents
.is();
136 m_bOwnClipboardChange
= true;
137 QApplication::clipboard()->setMimeData(new QtMimeData(m_aContents
), m_aClipboardMode
);
138 m_bOwnClipboardChange
= false;
142 assert(!m_aOwner
.is());
143 Q_EMIT
clearClipboard();
148 // we have to notify only an owner change, since handleChanged can't
149 // access the previous owner anymore and can just handle lost ownership.
150 if (xOldOwner
.is() && xOldOwner
!= xClipboardOwner
)
151 xOldOwner
->lostOwnership(this, xOldContents
);
154 void QtClipboard::handleChanged(QClipboard::Mode aMode
)
156 if (aMode
!= m_aClipboardMode
)
159 osl::ClearableMutexGuard
aGuard(m_aMutex
);
161 // QtWayland will send a second change notification (seemingly without any
162 // trigger). And any C'n'P operation in the Qt file picker emits a signal,
163 // with LO still holding the clipboard ownership, but internally having lost
164 // it. So ignore any signal, which still delivers the internal QtMimeData
165 // as the clipboard content and is no "advertised" change.
166 if (!m_bOwnClipboardChange
&& isOwner(aMode
)
167 && dynamic_cast<const QtMimeData
*>(QApplication::clipboard()->mimeData(aMode
)))
170 css::uno::Reference
<css::datatransfer::clipboard::XClipboardOwner
> xOldOwner(m_aOwner
);
171 css::uno::Reference
<css::datatransfer::XTransferable
> xOldContents(m_aContents
);
172 // ownership change from LO POV is handled in setContents
173 if (!m_bOwnClipboardChange
)
179 std::vector
<css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>> aListeners(
181 css::datatransfer::clipboard::ClipboardEvent aEv
;
182 aEv
.Contents
= getContents();
186 if (!m_bOwnClipboardChange
&& xOldOwner
.is())
187 xOldOwner
->lostOwnership(this, xOldContents
);
188 for (auto const& listener
: aListeners
)
189 listener
->changedContents(aEv
);
192 OUString
QtClipboard::getImplementationName() { return "com.sun.star.datatransfer.QtClipboard"; }
194 css::uno::Sequence
<OUString
> QtClipboard::getSupportedServiceNames()
196 return { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
199 sal_Bool
QtClipboard::supportsService(const OUString
& ServiceName
)
201 return cppu::supportsService(this, ServiceName
);
204 OUString
QtClipboard::getName() { return m_aClipboardName
; }
206 sal_Int8
QtClipboard::getRenderingCapabilities() { return 0; }
208 void QtClipboard::addClipboardListener(
209 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>& listener
)
211 osl::MutexGuard
aGuard(m_aMutex
);
212 m_aListeners
.push_back(listener
);
215 void QtClipboard::removeClipboardListener(
216 const css::uno::Reference
<css::datatransfer::clipboard::XClipboardListener
>& listener
)
218 osl::MutexGuard
aGuard(m_aMutex
);
219 m_aListeners
.erase(std::remove(m_aListeners
.begin(), m_aListeners
.end(), listener
),
223 bool QtClipboard::isSupported(const QClipboard::Mode aMode
)
225 const QClipboard
* pClipboard
= QApplication::clipboard();
228 case QClipboard::Selection
:
229 return pClipboard
->supportsSelection();
231 case QClipboard::FindBuffer
:
232 return pClipboard
->supportsFindBuffer();
234 case QClipboard::Clipboard
:
240 bool QtClipboard::isOwner(const QClipboard::Mode aMode
)
242 if (!isSupported(aMode
))
245 const QClipboard
* pClipboard
= QApplication::clipboard();
248 case QClipboard::Selection
:
249 return pClipboard
->ownsSelection();
251 case QClipboard::FindBuffer
:
252 return pClipboard
->ownsFindBuffer();
254 case QClipboard::Clipboard
:
255 return pClipboard
->ownsClipboard();
260 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */