Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / vcl / qt5 / QtClipboard.cxx
bloba61273a734bd864c8894e6dae154cefa92f49fd1
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4; fill-column: 100 -*- */
2 /*
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/.
9 */
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>
23 #include <cassert>
24 #include <map>
25 #include <utility>
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)
34 , m_bDoClear(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 cppu::getXWeak(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 SolarMutexGuard g;
63 QtInstance& rQtInstance = GetQtInstance();
64 rQtInstance.RunInMainThread([this]() {
65 if (!isOwner(m_aClipboardMode))
66 return;
68 QClipboard* pClipboard = QApplication::clipboard();
69 const QtMimeData* pQtMimeData
70 = qobject_cast<const QtMimeData*>(pClipboard->mimeData(m_aClipboardMode));
71 assert(pQtMimeData);
73 QMimeData* pMimeCopy = nullptr;
74 if (pQtMimeData && pQtMimeData->deepCopy(&pMimeCopy))
76 m_bOwnClipboardChange = true;
77 pClipboard->setMimeData(pMimeCopy, m_aClipboardMode);
78 m_bOwnClipboardChange = false;
80 });
83 css::uno::Reference<css::datatransfer::XTransferable> QtClipboard::getContents()
85 #if defined(EMSCRIPTEN)
86 static QMimeData aMimeData;
87 #endif
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())
94 return m_aContents;
96 // check if we can still use the shared QtClipboardTransferable
97 const QMimeData* pMimeData = QApplication::clipboard()->mimeData(m_aClipboardMode);
98 #if defined(EMSCRIPTEN)
99 if (!pMimeData)
100 pMimeData = &aMimeData;
101 #endif
102 if (m_aContents.is())
104 const auto* pTrans = dynamic_cast<QtClipboardTransferable*>(m_aContents.get());
105 assert(pTrans);
106 if (pTrans && pTrans->hasMimeData(pMimeData))
107 return m_aContents;
110 m_aContents = new QtClipboardTransferable(m_aClipboardMode, pMimeData);
111 return m_aContents;
114 void QtClipboard::handleClearClipboard()
116 if (!m_bDoClear)
117 return;
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();
134 if (!m_bDoClear)
136 m_bOwnClipboardChange = true;
137 QApplication::clipboard()->setMimeData(new QtMimeData(m_aContents), m_aClipboardMode);
138 m_bOwnClipboardChange = false;
140 else
142 assert(!m_aOwner.is());
143 Q_EMIT clearClipboard();
146 aGuard.clear();
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)
157 return;
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 && qobject_cast<const QtMimeData*>(QApplication::clipboard()->mimeData(aMode)))
168 return;
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)
175 m_aContents.clear();
176 m_aOwner.clear();
179 std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> aListeners(
180 m_aListeners);
181 css::datatransfer::clipboard::ClipboardEvent aEv;
182 aEv.Contents = getContents();
184 aGuard.clear();
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()
194 return u"com.sun.star.datatransfer.QtClipboard"_ustr;
197 css::uno::Sequence<OUString> QtClipboard::getSupportedServiceNames()
199 return { u"com.sun.star.datatransfer.clipboard.SystemClipboard"_ustr };
202 sal_Bool QtClipboard::supportsService(const OUString& ServiceName)
204 return cppu::supportsService(this, ServiceName);
207 OUString QtClipboard::getName() { return m_aClipboardName; }
209 sal_Int8 QtClipboard::getRenderingCapabilities() { return 0; }
211 void QtClipboard::addClipboardListener(
212 const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
214 osl::MutexGuard aGuard(m_aMutex);
215 m_aListeners.push_back(listener);
218 void QtClipboard::removeClipboardListener(
219 const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener)
221 osl::MutexGuard aGuard(m_aMutex);
222 std::erase(m_aListeners, listener);
225 bool QtClipboard::isSupported(const QClipboard::Mode aMode)
227 const QClipboard* pClipboard = QApplication::clipboard();
228 switch (aMode)
230 case QClipboard::Selection:
231 return pClipboard->supportsSelection();
233 case QClipboard::FindBuffer:
234 return pClipboard->supportsFindBuffer();
236 case QClipboard::Clipboard:
237 return true;
239 return false;
242 bool QtClipboard::isOwner(const QClipboard::Mode aMode)
244 if (!isSupported(aMode))
245 return false;
247 const QClipboard* pClipboard = QApplication::clipboard();
248 switch (aMode)
250 case QClipboard::Selection:
251 return pClipboard->ownsSelection();
253 case QClipboard::FindBuffer:
254 return pClipboard->ownsFindBuffer();
256 case QClipboard::Clipboard:
257 return pClipboard->ownsClipboard();
259 return false;
262 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */