tdf#163962 Enable spell checking in editable sections in read-only mode
[LibreOffice.git] / vcl / win / dtrans / WinClipboard.cxx
blob0cc55a23b6f09fac439721aae938f8dd85dbfad4
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <o3tl/test_info.hxx>
23 #include <osl/diagnose.h>
24 #include <comphelper/diagnose_ex.hxx>
25 #include <com/sun/star/datatransfer/clipboard/ClipboardEvent.hpp>
26 #include <com/sun/star/lang/DisposedException.hpp>
27 #include <com/sun/star/lang/IllegalArgumentException.hpp>
28 #include <com/sun/star/uno/XComponentContext.hpp>
29 #include <cppuhelper/supportsservice.hxx>
30 #include <cppuhelper/weak.hxx>
31 #include <vcl/svapp.hxx>
32 #include <svdata.hxx>
33 #include <salinst.hxx>
35 #include <com/sun/star/datatransfer/clipboard/RenderingCapabilities.hpp>
36 #include "XNotifyingDataObject.hxx"
38 #include <systools/win32/comtools.hxx>
39 #include "DtObjFactory.hxx"
40 #include "APNDataObject.hxx"
41 #include "DOTransferable.hxx"
42 #include "WinClipboard.hxx"
44 #if !defined WIN32_LEAN_AND_MEAN
45 #define WIN32_LEAN_AND_MEAN
46 #endif
47 #include <windows.h>
48 #include <ole2.h>
49 #include <objidl.h>
51 using namespace com::sun::star;
53 namespace
55 CWinClipboard* s_pCWinClipbImpl = nullptr;
56 std::mutex s_aClipboardSingletonMutex;
58 unsigned __stdcall releaseAsyncProc(void* p)
60 static_cast<css::datatransfer::XTransferable*>(p)->release();
61 return 0;
64 void releaseAsync(css::uno::Reference<css::datatransfer::XTransferable>& ref)
66 if (!ref)
67 return;
68 auto pInterface = ref.get();
69 pInterface->acquire();
70 ref.clear(); // before starting the thread, to avoid race
71 if (auto handle = _beginthreadex(nullptr, 0, releaseAsyncProc, pInterface, 0, nullptr))
72 CloseHandle(reinterpret_cast<HANDLE>(handle));
76 /*XEventListener,*/
77 CWinClipboard::CWinClipboard(const uno::Reference<uno::XComponentContext>& rxContext,
78 const OUString& aClipboardName)
79 : m_xContext(rxContext)
80 , m_itsName(aClipboardName)
82 // necessary to reassociate from
83 // the static callback function
85 std::unique_lock aGuard(s_aClipboardSingletonMutex);
86 s_pCWinClipbImpl = this;
89 registerClipboardViewer();
92 CWinClipboard::~CWinClipboard()
94 assert(m_bDisposed);
95 assert(!s_pCWinClipbImpl);
98 void CWinClipboard::disposing(std::unique_lock<std::mutex>& mutex)
101 std::unique_lock aGuard(s_aClipboardSingletonMutex);
102 s_pCWinClipbImpl = nullptr;
105 unregisterClipboardViewer();
107 WeakComponentImplHelper::disposing(mutex);
110 // XClipboard
112 CXNotifyingDataObject* CWinClipboard::getOwnClipContent() const
114 assert(!m_pCurrentOwnClipContent || !m_pNewOwnClipContent); // Both can be null, or only one set
115 return m_pCurrentOwnClipContent ? m_pCurrentOwnClipContent : m_pNewOwnClipContent;
118 // to avoid unnecessary traffic we check first if there is a clipboard
119 // content which was set via setContent, in this case we don't need
120 // to query the content from the clipboard, create a new wrapper object
121 // and so on, we simply return the original XTransferable instead of our
122 // DOTransferable
124 uno::Reference<datatransfer::XTransferable> SAL_CALL CWinClipboard::getContents()
126 std::unique_lock aGuard(m_aMutex);
127 return getContents_noLock();
130 css::uno::Reference<css::datatransfer::XTransferable> CWinClipboard::getContents_noLock()
132 if (m_bDisposed)
133 throw lang::DisposedException("object is already disposed",
134 static_cast<XClipboardEx*>(this));
136 assert(!getOwnClipContent() || !m_foreignContent); // Both can be null, or only one set
138 // use the shortcut or create a transferable from
139 // system clipboard
140 if (auto pOwnClipContent = getOwnClipContent())
141 return pOwnClipContent->m_XTransferable;
143 // Content cached?
144 if (m_foreignContent.is())
145 return m_foreignContent;
147 uno::Reference<datatransfer::XTransferable> rClipContent;
149 // get the current format list from clipboard
150 if (UINT nFormats; !GetUpdatedClipboardFormats(nullptr, 0, &nFormats)
151 && GetLastError() == ERROR_INSUFFICIENT_BUFFER)
153 std::vector<UINT> aUINTFormats(nFormats);
154 if (GetUpdatedClipboardFormats(aUINTFormats.data(), nFormats, &nFormats))
156 std::vector<sal_uInt32> aFormats(aUINTFormats.begin(), aUINTFormats.end());
157 rClipContent = new CDOTransferable(m_xContext, this, aFormats);
159 m_foreignContent = rClipContent;
163 return rClipContent;
166 IDataObjectPtr CWinClipboard::getIDataObject()
169 std::unique_lock aGuard(m_aMutex);
171 if (m_bDisposed)
172 throw lang::DisposedException("object is already disposed",
173 static_cast<XClipboardEx*>(this));
175 // get the current dataobject from clipboard
176 IDataObjectPtr pIDataObject;
177 HRESULT hr = m_MtaOleClipboard.getClipboard(&pIDataObject);
179 if (SUCCEEDED(hr))
181 // create an apartment neutral dataobject and initialize it with a
182 // com smart pointer to the IDataObject from clipboard
183 pIDataObject = new CAPNDataObject(pIDataObject);
186 return pIDataObject;
189 void SAL_CALL CWinClipboard::setContents(
190 const uno::Reference<datatransfer::XTransferable>& xTransferable,
191 const uno::Reference<datatransfer::clipboard::XClipboardOwner>& xClipboardOwner)
193 std::unique_lock aGuard(m_aMutex);
195 if (m_bDisposed)
196 throw lang::DisposedException("object is already disposed",
197 static_cast<XClipboardEx*>(this));
199 IDataObjectPtr pIDataObj;
201 // The object must be destroyed only outside of the mutex lock, and in a different thread,
202 // because it may call CWinClipboard::onReleaseDataObject, or try to lock solar mutex, in
203 // another thread of this process synchronously
204 releaseAsync(m_foreignContent); // clear m_foreignContent
205 assert(!m_foreignContent.is());
206 m_pCurrentOwnClipContent = nullptr;
208 if (xTransferable.is())
210 // Store the new object's pointer to temporary m_pNewOwnClipContent, to be moved to
211 // m_pCurrentOwnClipContent in handleClipboardContentChanged.
212 m_pNewOwnClipContent = new CXNotifyingDataObject(
213 CDTransObjFactory::createDataObjFromTransferable(m_xContext, xTransferable),
214 xTransferable, xClipboardOwner, this);
216 pIDataObj = IDataObjectPtr(m_pNewOwnClipContent);
218 else
220 m_pNewOwnClipContent = nullptr;
223 m_MtaOleClipboard.setClipboard(pIDataObj.get());
226 OUString SAL_CALL CWinClipboard::getName()
228 std::unique_lock aGuard(m_aMutex);
229 if (m_bDisposed)
230 throw lang::DisposedException("object is already disposed",
231 static_cast<XClipboardEx*>(this));
233 return m_itsName;
236 // XFlushableClipboard
238 void SAL_CALL CWinClipboard::flushClipboard()
240 std::unique_lock aGuard(m_aMutex);
242 if (m_bDisposed)
243 throw lang::DisposedException("object is already disposed",
244 static_cast<XClipboardEx*>(this));
246 // FlushClipboard does a callback and frees DataObject, which calls onReleaseDataObject and
247 // locks mutex. FlushClipboard has to be synchron in order to prevent shutdown until all
248 // clipboard-formats are rendered. The request is needed to prevent flushing if we are not
249 // clipboard owner (it is not known what happens if we flush but aren't clipboard owner).
250 // It may be possible to move the request to the clipboard STA thread by saving the
251 // DataObject and call OleIsCurrentClipboard before flushing.
253 if (getOwnClipContent())
255 aGuard.unlock();
256 m_MtaOleClipboard.flushClipboard();
260 // XClipboardEx
262 sal_Int8 SAL_CALL CWinClipboard::getRenderingCapabilities()
264 if (m_bDisposed)
265 throw lang::DisposedException("object is already disposed",
266 static_cast<XClipboardEx*>(this));
268 using namespace datatransfer::clipboard::RenderingCapabilities;
269 return (Delayed | Persistent);
272 // XClipboardNotifier
274 void SAL_CALL CWinClipboard::addClipboardListener(
275 const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener)
277 std::unique_lock aGuard(m_aMutex);
278 if (m_bDisposed)
279 throw lang::DisposedException("object is already disposed",
280 static_cast<XClipboardEx*>(this));
282 // check input parameter
283 if (!listener.is())
284 throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this),
287 maClipboardListeners.addInterface(aGuard, listener);
290 void SAL_CALL CWinClipboard::removeClipboardListener(
291 const uno::Reference<datatransfer::clipboard::XClipboardListener>& listener)
293 std::unique_lock aGuard(m_aMutex);
294 if (m_bDisposed)
295 throw lang::DisposedException("object is already disposed",
296 static_cast<XClipboardEx*>(this));
298 // check input parameter
299 if (!listener.is())
300 throw lang::IllegalArgumentException("empty reference", static_cast<XClipboardEx*>(this),
303 maClipboardListeners.removeInterface(aGuard, listener);
306 void CWinClipboard::handleClipboardContentChanged()
308 std::unique_lock aGuard(m_aMutex);
309 if (m_bDisposed)
310 return;
312 // The object must be destroyed only outside of the mutex lock, and in a different thread,
313 // because it may call CWinClipboard::onReleaseDataObject, or try to lock solar mutex, in
314 // another thread of this process synchronously
315 releaseAsync(m_foreignContent); // clear m_foreignContent
316 assert(!m_foreignContent.is());
317 // If new own content assignment is pending, do it; otherwise, clear it.
318 // This makes sure that there will be no stuck clipboard content.
319 m_pCurrentOwnClipContent = std::exchange(m_pNewOwnClipContent, nullptr);
321 if (!maClipboardListeners.getLength(aGuard))
322 return;
326 uno::Reference<datatransfer::XTransferable> rXTransf(getContents_noLock());
327 datatransfer::clipboard::ClipboardEvent aClipbEvent(static_cast<XClipboard*>(this),
328 rXTransf);
329 maClipboardListeners.notifyEach(
330 aGuard, &datatransfer::clipboard::XClipboardListener::changedContents, aClipbEvent);
331 aGuard.unlock(); // for XTransferable dtor, that may delegate to another thread
333 catch (const lang::DisposedException&)
335 OSL_FAIL("Service Manager disposed");
336 if (aGuard.owns_lock())
337 aGuard.unlock();
338 // no further clipboard changed notifications
339 unregisterClipboardViewer();
343 // XServiceInfo
345 OUString SAL_CALL CWinClipboard::getImplementationName()
347 return "com.sun.star.datatransfer.clipboard.ClipboardW32";
350 sal_Bool SAL_CALL CWinClipboard::supportsService(const OUString& ServiceName)
352 return cppu::supportsService(this, ServiceName);
355 uno::Sequence<OUString> SAL_CALL CWinClipboard::getSupportedServiceNames()
357 return { "com.sun.star.datatransfer.clipboard.SystemClipboard" };
360 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
361 dtrans_CWinClipboard_get_implementation(css::uno::XComponentContext* context,
362 css::uno::Sequence<css::uno::Any> const& args)
364 // We run unit tests in parallel, which is a problem when touching a shared resource
365 // like the system clipboard, so rather use the dummy GenericClipboard.
366 static const bool bRunningUnitTest = o3tl::IsRunningUnitTest() || o3tl::IsRunningUITest();
368 if (bRunningUnitTest)
370 SolarMutexGuard aGuard;
371 auto xClipboard = ImplGetSVData()->mpDefInst->CreateClipboard(args);
372 if (xClipboard.is())
373 xClipboard->acquire();
374 return xClipboard.get();
376 else
378 return cppu::acquire(new CWinClipboard(context, ""));
382 void CWinClipboard::onReleaseDataObject(CXNotifyingDataObject& theCaller)
384 theCaller.lostOwnership();
386 // if the current caller is the one we currently hold, then set it to NULL
387 // because an external source must be the clipboardowner now
388 std::unique_lock aGuard(m_aMutex);
390 if (getOwnClipContent() == &theCaller)
391 m_pCurrentOwnClipContent = m_pNewOwnClipContent = nullptr;
394 void CWinClipboard::registerClipboardViewer()
396 m_MtaOleClipboard.registerClipViewer(CWinClipboard::onClipboardContentChanged);
399 void CWinClipboard::unregisterClipboardViewer() { m_MtaOleClipboard.registerClipViewer(nullptr); }
401 void WINAPI CWinClipboard::onClipboardContentChanged()
403 rtl::Reference<CWinClipboard> pWinClipboard;
405 // Only hold the mutex to obtain a safe reference to the impl, to avoid deadlock
406 std::unique_lock aGuard(s_aClipboardSingletonMutex);
407 pWinClipboard.set(s_pCWinClipbImpl);
410 if (pWinClipboard)
411 pWinClipboard->handleClipboardContentChanged();
414 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */