calc: on editing invalidation of view with different zoom is wrong
[LibreOffice.git] / vcl / qt5 / QtTransferable.cxx
blobd9e0beaa71d3c32602b5774a2d7f60dead36fa02
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 */
11 #include <QtTransferable.hxx>
13 #include <comphelper/sequence.hxx>
14 #include <sal/log.hxx>
15 #include <o3tl/string_view.hxx>
17 #include <QtWidgets/QApplication>
19 #include <QtInstance.hxx>
20 #include <QtTools.hxx>
22 #include <cassert>
24 static bool lcl_textMimeInfo(std::u16string_view rMimeString, bool& bHaveNoCharset,
25 bool& bHaveUTF16, bool& bHaveUTF8)
27 sal_Int32 nIndex = 0;
28 if (o3tl::getToken(rMimeString, 0, ';', nIndex) == u"text/plain")
30 std::u16string_view aToken(o3tl::getToken(rMimeString, 0, ';', nIndex));
31 if (aToken == u"charset=utf-16")
32 bHaveUTF16 = true;
33 else if (aToken == u"charset=utf-8")
34 bHaveUTF8 = true;
35 else if (aToken.empty())
36 bHaveNoCharset = true;
37 else // we just handle UTF-16 and UTF-8, everything else is "bytes"
38 return false;
39 return true;
41 return false;
44 QtTransferable::QtTransferable(const QMimeData* pMimeData)
45 : m_pMimeData(pMimeData)
46 , m_bProvideUTF16FromOtherEncoding(false)
48 assert(pMimeData);
51 css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL QtTransferable::getTransferDataFlavors()
53 // it's just filled once, ever, so just try to get it without locking first
54 if (m_aMimeTypeSeq.hasElements())
55 return m_aMimeTypeSeq;
57 // better safe then sorry; preventing broken usage
58 // DnD should not be shared and Clipboard access runs in the GUI thread
59 osl::MutexGuard aGuard(m_aMutex);
60 if (m_aMimeTypeSeq.hasElements())
61 return m_aMimeTypeSeq;
63 QStringList aFormatList(m_pMimeData->formats());
64 // we might add the UTF-16 mime text variant later
65 const int nMimeTypeSeqSize = aFormatList.size() + 1;
66 bool bHaveNoCharset = false, bHaveUTF16 = false, bHaveUTF8 = false;
67 css::uno::Sequence<css::datatransfer::DataFlavor> aMimeTypeSeq(nMimeTypeSeqSize);
68 auto pMimeTypeSeq = aMimeTypeSeq.getArray();
70 css::datatransfer::DataFlavor aFlavor;
71 int nMimeTypeCount = 0;
73 for (const QString& rMimeType : aFormatList)
75 // filter out non-MIME types such as TARGETS, MULTIPLE, TIMESTAMP
76 if (rMimeType.indexOf('/') == -1)
77 continue;
79 // gtk3 thinks it is not well defined - skip too
80 if (rMimeType == QStringLiteral("text/plain;charset=unicode"))
81 continue;
83 // LO doesn't like 'text/plain', so we have to provide UTF-16
84 bool bIsNoCharset = false, bIsUTF16 = false, bIsUTF8 = false;
85 if (lcl_textMimeInfo(toOUString(rMimeType), bIsNoCharset, bIsUTF16, bIsUTF8))
87 bHaveNoCharset |= bIsNoCharset;
88 bHaveUTF16 |= bIsUTF16;
89 bHaveUTF8 |= bIsUTF8;
90 if (bIsUTF16)
91 aFlavor.DataType = cppu::UnoType<OUString>::get();
92 else
93 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
95 else
96 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
98 aFlavor.MimeType = toOUString(rMimeType);
99 assert(nMimeTypeCount < nMimeTypeSeqSize);
100 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
101 nMimeTypeCount++;
104 m_bProvideUTF16FromOtherEncoding = (bHaveNoCharset || bHaveUTF8) && !bHaveUTF16;
105 if (m_bProvideUTF16FromOtherEncoding)
107 aFlavor.MimeType = "text/plain;charset=utf-16";
108 aFlavor.DataType = cppu::UnoType<OUString>::get();
109 assert(nMimeTypeCount < nMimeTypeSeqSize);
110 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
111 nMimeTypeCount++;
114 aMimeTypeSeq.realloc(nMimeTypeCount);
116 m_aMimeTypeSeq = aMimeTypeSeq;
117 return m_aMimeTypeSeq;
120 sal_Bool SAL_CALL
121 QtTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
123 const auto aSeq = getTransferDataFlavors();
124 return std::any_of(aSeq.begin(), aSeq.end(), [&](const css::datatransfer::DataFlavor& aFlavor) {
125 return rFlavor.MimeType == aFlavor.MimeType;
129 css::uno::Any SAL_CALL QtTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
131 css::uno::Any aAny;
132 if (!isDataFlavorSupported(rFlavor))
133 return aAny;
135 if (rFlavor.MimeType == "text/plain;charset=utf-16")
137 OUString aString;
138 if (m_bProvideUTF16FromOtherEncoding)
140 if (m_pMimeData->hasFormat("text/plain;charset=utf-8"))
142 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain;charset=utf-8")));
143 aString = OUString::fromUtf8(reinterpret_cast<const char*>(aByteData.data()));
145 else
147 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain")));
148 aString = OUString(reinterpret_cast<const char*>(aByteData.data()),
149 aByteData.size(), osl_getThreadTextEncoding());
152 else
154 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
155 aString = OUString(reinterpret_cast<const sal_Unicode*>(aByteData.data()),
156 aByteData.size() / 2);
158 aAny <<= aString;
160 else
162 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
163 css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(aByteData.data()),
164 aByteData.size());
165 aAny <<= aSeq;
168 return aAny;
171 QtClipboardTransferable::QtClipboardTransferable(const QClipboard::Mode aMode,
172 const QMimeData* pMimeData)
173 : QtTransferable(pMimeData)
174 , m_aMode(aMode)
178 bool QtClipboardTransferable::hasInFlightChanged() const
180 const bool bChanged(mimeData() != QApplication::clipboard()->mimeData(m_aMode));
181 SAL_WARN_IF(bChanged, "vcl.qt", "In flight clipboard change detected - broken clipboard read!");
182 return bChanged;
185 css::uno::Any SAL_CALL
186 QtClipboardTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
188 css::uno::Any aAny;
189 auto* pSalInst(GetQtInstance());
190 SolarMutexGuard g;
191 pSalInst->RunInMainThread([&, this]() {
192 if (!hasInFlightChanged())
193 aAny = QtTransferable::getTransferData(rFlavor);
195 return aAny;
198 css::uno::Sequence<css::datatransfer::DataFlavor>
199 SAL_CALL QtClipboardTransferable::getTransferDataFlavors()
201 css::uno::Sequence<css::datatransfer::DataFlavor> aSeq;
202 auto* pSalInst(GetQtInstance());
203 SolarMutexGuard g;
204 pSalInst->RunInMainThread([&, this]() {
205 if (!hasInFlightChanged())
206 aSeq = QtTransferable::getTransferDataFlavors();
208 return aSeq;
211 sal_Bool SAL_CALL
212 QtClipboardTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
214 bool bIsSupported = false;
215 auto* pSalInst(GetQtInstance());
216 SolarMutexGuard g;
217 pSalInst->RunInMainThread([&, this]() {
218 if (!hasInFlightChanged())
219 bIsSupported = QtTransferable::isDataFlavorSupported(rFlavor);
221 return bIsSupported;
224 QtMimeData::QtMimeData(const css::uno::Reference<css::datatransfer::XTransferable>& xTrans)
225 : m_aContents(xTrans)
226 , m_bHaveNoCharset(false)
227 , m_bHaveUTF8(false)
229 assert(xTrans.is());
232 bool QtMimeData::deepCopy(QMimeData** const pMimeCopy) const
234 if (!pMimeCopy)
235 return false;
237 QMimeData* pMimeData = new QMimeData();
238 for (QString& format : formats())
240 QByteArray aData = data(format);
241 // Checking for custom MIME types
242 if (format.startsWith("application/x-qt"))
244 // Retrieving true format name
245 int indexBegin = format.indexOf('"') + 1;
246 int indexEnd = format.indexOf('"', indexBegin);
247 format = format.mid(indexBegin, indexEnd - indexBegin);
249 pMimeData->setData(format, aData);
252 *pMimeCopy = pMimeData;
253 return true;
256 QStringList QtMimeData::formats() const
258 if (!m_aMimeTypeList.isEmpty())
259 return m_aMimeTypeList;
261 const css::uno::Sequence<css::datatransfer::DataFlavor> aFormats
262 = m_aContents->getTransferDataFlavors();
263 QStringList aList;
264 bool bHaveUTF16 = false;
266 for (const auto& rFlavor : aFormats)
268 aList << toQString(rFlavor.MimeType);
269 lcl_textMimeInfo(rFlavor.MimeType, m_bHaveNoCharset, bHaveUTF16, m_bHaveUTF8);
272 // we provide a locale encoded and a UTF-8 variant, if missing
273 if (m_bHaveNoCharset || bHaveUTF16 || m_bHaveUTF8)
275 // if there is a text representation from LO point of view, it'll be UTF-16
276 assert(bHaveUTF16);
277 if (!m_bHaveUTF8)
278 aList << QStringLiteral("text/plain;charset=utf-8");
279 if (!m_bHaveNoCharset)
280 aList << QStringLiteral("text/plain");
283 m_aMimeTypeList = aList;
284 return m_aMimeTypeList;
287 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
288 QVariant QtMimeData::retrieveData(const QString& mimeType, QVariant::Type) const
289 #else
290 QVariant QtMimeData::retrieveData(const QString& mimeType, QMetaType) const
291 #endif
293 if (!hasFormat(mimeType))
294 return QVariant();
296 css::datatransfer::DataFlavor aFlavor;
297 aFlavor.MimeType = toOUString(mimeType);
298 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
300 bool bWantNoCharset = false, bWantUTF16 = false, bWantUTF8 = false;
301 if (lcl_textMimeInfo(aFlavor.MimeType, bWantNoCharset, bWantUTF16, bWantUTF8))
303 if ((bWantNoCharset && !m_bHaveNoCharset) || (bWantUTF8 && !m_bHaveUTF8))
305 aFlavor.MimeType = "text/plain;charset=utf-16";
306 aFlavor.DataType = cppu::UnoType<OUString>::get();
308 else if (bWantUTF16)
309 aFlavor.DataType = cppu::UnoType<OUString>::get();
312 css::uno::Any aValue;
316 // tdf#129809 take a reference in case m_aContents is replaced during this call
317 css::uno::Reference<com::sun::star::datatransfer::XTransferable> xCurrentContents(
318 m_aContents);
319 aValue = xCurrentContents->getTransferData(aFlavor);
321 catch (...)
325 QByteArray aByteArray;
326 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING)
328 OUString aString;
329 aValue >>= aString;
331 if (bWantUTF8)
333 OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
334 aByteArray = QByteArray(aUTF8String.getStr(), aUTF8String.getLength());
336 else if (bWantNoCharset)
338 OString aLocaleString(OUStringToOString(aString, osl_getThreadTextEncoding()));
339 aByteArray = QByteArray(aLocaleString.getStr(), aLocaleString.getLength());
341 else if (bWantUTF16)
343 aByteArray = QByteArray(reinterpret_cast<const char*>(aString.getStr()),
344 aString.getLength() * 2);
346 else
347 return QVariant(toQString(aString));
349 else
351 css::uno::Sequence<sal_Int8> aData;
352 aValue >>= aData;
353 aByteArray
354 = QByteArray(reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength());
356 return QVariant::fromValue(aByteArray);
359 bool QtMimeData::hasFormat(const QString& mimeType) const { return formats().contains(mimeType); }
361 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */