diff options
author | Jan-Marek Glogowski <glogow@fbihome.de> | 2019-05-31 16:40:34 +0000 |
---|---|---|
committer | Thorsten Behrens <Thorsten.Behrens@CIB.de> | 2019-06-13 23:18:58 +0200 |
commit | bcca1cf28cbd6c961d59bd8b8a8e58184dfc3823 (patch) | |
tree | a0ea40c0ab19a32f4da2a7247631454006378692 /vcl/inc/qt5 | |
parent | 1b39bae5debe3bf952449bd7d2910defcea92b9e (diff) |
tdf#122239 Qt5 implement lazy clipboard handling
This changes the Qt5Clipboard to a lazy loading one, which will
just deliver data on read requests. This fixes not only the
PRIMARY selection problems with Writer, but will generally speed
up C'n'P, inside LO, because the data has not to be copied or
transferred via QMimeData.
This is mainly done by implementing the "mirror" interface of the
Qt5Transferable, the Qt5MimeData using the retrieveData override.
To prevent clipboard loss on shutdown, this sets a deep copied
QMimeData of the current XTransferable, to make it persistent.
This code explicitly doesn't use any of the QMimeData convenience
functions and relies completely on LO's string handling, so we
won't mix in eventual Qt bugs; all bugs are ours...
Change-Id: I43d92a95df8fcac88dc41b00021cea0b5f040413
Reviewed-on: https://gerrit.libreoffice.org/73288
Tested-by: Jenkins
Reviewed-by: Michael Weghorn <m.weghorn@posteo.de>
Reviewed-by: Thorsten Behrens <Thorsten.Behrens@CIB.de>
Diffstat (limited to 'vcl/inc/qt5')
-rw-r--r-- | vcl/inc/qt5/Qt5Clipboard.hxx | 56 | ||||
-rw-r--r-- | vcl/inc/qt5/Qt5Transferable.hxx | 90 |
2 files changed, 104 insertions, 42 deletions
diff --git a/vcl/inc/qt5/Qt5Clipboard.hxx b/vcl/inc/qt5/Qt5Clipboard.hxx index 2c2bb93f5016..93ad36a0e672 100644 --- a/vcl/inc/qt5/Qt5Clipboard.hxx +++ b/vcl/inc/qt5/Qt5Clipboard.hxx @@ -20,7 +20,14 @@ #include <QtGui/QClipboard> -class Qt5Clipboard +/** + * This implementation has two main functions, which handle the clipboard content: + * the XClipboard::setContent function and the QClipboard::change signal handler. + * + * The first just sets the respective clipboard to the expected content from LO, + * the latter will handle any reported changes. + **/ +class Qt5Clipboard final : public QObject, public cppu::WeakComponentImplHelper<css::datatransfer::clipboard::XSystemClipboard, css::datatransfer::clipboard::XFlushableClipboard, @@ -29,61 +36,50 @@ class Qt5Clipboard Q_OBJECT osl::Mutex m_aMutex; + const OUString m_aClipboardName; + const QClipboard::Mode m_aClipboardMode; + + // if not empty, this holds the setContents provided XTransferable or a Qt5ClipboardTransferable css::uno::Reference<css::datatransfer::XTransferable> m_aContents; + // the owner of the current contents, which must be informed on content change css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner> m_aOwner; std::vector<css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>> m_aListeners; - OUString m_aClipboardName; - QClipboard::Mode m_aClipboardMode; - // custom MIME type to detect whether clipboard content was added by self or externally - const QString m_sMimeTypeUuid = "application/x-libreoffice-clipboard-uuid"; - const QByteArray m_aUuid; + + static bool isOwner(const QClipboard::Mode aMode); + static bool isSupported(const QClipboard::Mode aMode); + + explicit Qt5Clipboard(const OUString& aModeString, const QClipboard::Mode aMode); private Q_SLOTS: - void handleClipboardChange(QClipboard::Mode mode); + void handleChanged(QClipboard::Mode mode); public: - explicit Qt5Clipboard(const OUString& aModeString); - virtual ~Qt5Clipboard() override; - - /* - * XServiceInfo - */ + // factory function to construct only valid Qt5Clipboard objects by name + static css::uno::Reference<css::uno::XInterface> create(const OUString& aModeString); + // XServiceInfo virtual OUString SAL_CALL getImplementationName() override; virtual sal_Bool SAL_CALL supportsService(const OUString& ServiceName) override; virtual css::uno::Sequence<OUString> SAL_CALL getSupportedServiceNames() override; - /* - * XClipboard - */ - + // XClipboard virtual css::uno::Reference<css::datatransfer::XTransferable> SAL_CALL getContents() override; - virtual void SAL_CALL setContents( const css::uno::Reference<css::datatransfer::XTransferable>& xTrans, const css::uno::Reference<css::datatransfer::clipboard::XClipboardOwner>& xClipboardOwner) override; - virtual OUString SAL_CALL getName() override; - /* - * XClipboardEx - */ - + // XClipboardEx virtual sal_Int8 SAL_CALL getRenderingCapabilities() override; - /* - * XFlushableClipboard - */ + // XFlushableClipboard virtual void SAL_CALL flushClipboard() override; - /* - * XClipboardNotifier - */ + // XClipboardNotifier virtual void SAL_CALL addClipboardListener( const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) override; - virtual void SAL_CALL removeClipboardListener( const css::uno::Reference<css::datatransfer::clipboard::XClipboardListener>& listener) override; diff --git a/vcl/inc/qt5/Qt5Transferable.hxx b/vcl/inc/qt5/Qt5Transferable.hxx index edc14904f769..f36216eed121 100644 --- a/vcl/inc/qt5/Qt5Transferable.hxx +++ b/vcl/inc/qt5/Qt5Transferable.hxx @@ -13,44 +13,110 @@ #include <cppuhelper/compbase.hxx> #include <com/sun/star/datatransfer/XTransferable.hpp> +#include <QtCore/QMimeData> +#include <QtCore/QStringList> #include <QtGui/QClipboard> /** - * Abstract XTransferable used for clipboard and D'n'D transfers + * Qt5Transferable classes are used to read QMimeData via the XTransferable + * interface. All the functionality is already implemented in the Qt5Transferable. + * + * The specialisations map to the two users, which provide QMimeData: the Clipboard + * and the Drag'n'Drop functionality. + * + * LO itself seem to just accept "text/plain;charset=utf-16", so it relies on the + * backend to convert to this charset, but still offers "text/plain" itself. + * + * It's the "mirror" interface of the Qt5MimeData, which is defined below. **/ class Qt5Transferable : public cppu::WeakImplHelper<css::datatransfer::XTransferable> { - Qt5Transferable() = delete; Qt5Transferable(const Qt5Transferable&) = delete; -protected: const QMimeData* m_pMimeData; - - Qt5Transferable(const QMimeData* pMimeData); - std::vector<css::datatransfer::DataFlavor> getTransferDataFlavorsAsVector(); + osl::Mutex m_aMutex; + bool m_bConvertFromLocale; + css::uno::Sequence<css::datatransfer::DataFlavor> m_aMimeTypeSeq; public: - ~Qt5Transferable() override; + Qt5Transferable(const QMimeData* pMimeData); + const QMimeData* mimeData() const { return m_pMimeData; } css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() override; sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) override; + css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override; }; +/** + * The QClipboard's QMimeData is volatile. As written in the QClipboard::mimeData + * documentation, "the pointer returned might become invalidated when the contents + * of the clipboard changes". Therefore it can just be accessed reliably inside + * the QClipboard's object thread, which is the QApplication's thread, so all of + * the access has to go through RunInMainThread(). + * + * If we detect a QMimeData change, we simply drop reporting any content. In theory + * we can recover in the case where there hadn't been any calls of the XTransferable + * interface, but currently we don't. But we ensure to never report mixed content, + * so we'll just cease operation on QMimeData change. + **/ class Qt5ClipboardTransferable final : public Qt5Transferable { + // to detect in-flight QMimeData changes + const QClipboard::Mode m_aMode; + + bool hasInFlightChanged() const; + public: - explicit Qt5ClipboardTransferable(QClipboard::Mode aMode); - ~Qt5ClipboardTransferable() override; + explicit Qt5ClipboardTransferable(const QClipboard::Mode aMode, const QMimeData* pMimeData); + // these are the same then Qt5Transferable, except they go through RunInMainThread + css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL getTransferDataFlavors() override; + sal_Bool SAL_CALL isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor) override; css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override; }; -class Qt5DnDTransferable final : public Qt5Transferable +/** + * Convenience typedef for better code readability + * + * This just uses the QMimeData provided by the QWidgets D'n'D events. + **/ +typedef Qt5Transferable Qt5DnDTransferable; + +/** + * A lazy loading QMimeData for XTransferable reads + * + * This is an interface class to make a XTransferable read accessible as a + * QMimeData. The mime data is just stored inside the XTransferable, never + * in the QMimeData itself! It's objects are just used for QClipboard to read + * the XTransferable data. + * + * Like XTransferable itself, this class should be considered an immutable + * container for mime data. There is no need to ever set any of its data. + * + * LO will offer at least UTF-16, if there is a viable text representation. + * If LO misses to offer an UTF-8 or a locale encoded string, these objects + * will offer them themselves and convert from UTF-16 on demand. + * + * It's the "mirror" interface of the Qt5Transferable. + **/ +class Qt5MimeData final : public QMimeData { + friend class Qt5ClipboardTransferable; + + const css::uno::Reference<css::datatransfer::XTransferable> m_aContents; + mutable bool m_bHaveNoCharset; // = uses the locale charset + mutable bool m_bHaveUTF8; + mutable QStringList m_aMimeTypeList; + + QVariant retrieveData(const QString& mimeType, QVariant::Type type) const override; + public: - Qt5DnDTransferable(const QMimeData* pMimeData); + explicit Qt5MimeData(const css::uno::Reference<css::datatransfer::XTransferable>& aContents); - css::uno::Any SAL_CALL getTransferData(const css::datatransfer::DataFlavor& rFlavor) override; + bool hasFormat(const QString& mimeType) const override; + QStringList formats() const override; + + bool deepCopy(QMimeData** const) const; }; /* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |