Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / vcl / qt5 / QtTransferable.cxx
blob16f24763cf6ecd954a374cc5643cf274d37058be
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 <QtTransferable.hxx>
12 #include <QtTransferable.moc>
14 #include <comphelper/sequence.hxx>
15 #include <sal/log.hxx>
16 #include <o3tl/string_view.hxx>
17 #include <tools/debug.hxx>
18 #include <vcl/qt/QtUtils.hxx>
20 #include <QtWidgets/QApplication>
22 #include <QtInstance.hxx>
23 #include <QtTools.hxx>
25 #include <cassert>
27 static bool lcl_textMimeInfo(std::u16string_view rMimeString, bool& bHaveNoCharset,
28 bool& bHaveUTF16, bool& bHaveUTF8)
30 sal_Int32 nIndex = 0;
31 if (o3tl::getToken(rMimeString, 0, ';', nIndex) == u"text/plain")
33 std::u16string_view aToken(o3tl::getToken(rMimeString, 0, ';', nIndex));
34 if (aToken == u"charset=utf-16")
35 bHaveUTF16 = true;
36 else if (aToken == u"charset=utf-8")
37 bHaveUTF8 = true;
38 else if (aToken.empty())
39 bHaveNoCharset = true;
40 else // we just handle UTF-16 and UTF-8, everything else is "bytes"
41 return false;
42 return true;
44 return false;
47 QtTransferable::QtTransferable(const QMimeData* pMimeData)
48 : m_pMimeData(pMimeData)
50 assert(pMimeData);
53 css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL QtTransferable::getTransferDataFlavors()
55 if (!m_pMimeData)
56 return css::uno::Sequence<css::datatransfer::DataFlavor>();
58 QStringList aFormatList(m_pMimeData->formats());
59 // we might add the UTF-16 mime text variant later
60 const int nMimeTypeSeqSize = aFormatList.size() + 1;
61 bool bHaveNoCharset = false, bHaveUTF16 = false, bHaveUTF8 = false;
62 css::uno::Sequence<css::datatransfer::DataFlavor> aMimeTypeSeq(nMimeTypeSeqSize);
63 auto pMimeTypeSeq = aMimeTypeSeq.getArray();
65 css::datatransfer::DataFlavor aFlavor;
66 int nMimeTypeCount = 0;
68 for (const QString& rMimeType : aFormatList)
70 // filter out non-MIME types such as TARGETS, MULTIPLE, TIMESTAMP
71 if (rMimeType.indexOf('/') == -1)
72 continue;
74 // gtk3 thinks it is not well defined - skip too
75 if (rMimeType == QStringLiteral("text/plain;charset=unicode"))
76 continue;
78 // LO doesn't like 'text/plain', so we have to provide UTF-16
79 bool bIsNoCharset = false, bIsUTF16 = false, bIsUTF8 = false;
80 if (lcl_textMimeInfo(toOUString(rMimeType), bIsNoCharset, bIsUTF16, bIsUTF8))
82 bHaveNoCharset |= bIsNoCharset;
83 bHaveUTF16 |= bIsUTF16;
84 bHaveUTF8 |= bIsUTF8;
85 if (bIsUTF16)
86 aFlavor.DataType = cppu::UnoType<OUString>::get();
87 else
88 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
90 else
91 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
93 aFlavor.MimeType = toOUString(rMimeType);
94 assert(nMimeTypeCount < nMimeTypeSeqSize);
95 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
96 nMimeTypeCount++;
99 // in case of text/plain data, but no UTF-16 encoded one,
100 // QtTransferable::getTransferData converts from existing encoding to UTF-16
101 const bool bProvideUTF16FromOtherEncoding = (bHaveNoCharset || bHaveUTF8) && !bHaveUTF16;
102 if (bProvideUTF16FromOtherEncoding)
104 aFlavor.MimeType = "text/plain;charset=utf-16";
105 aFlavor.DataType = cppu::UnoType<OUString>::get();
106 assert(nMimeTypeCount < nMimeTypeSeqSize);
107 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
108 nMimeTypeCount++;
111 aMimeTypeSeq.realloc(nMimeTypeCount);
113 return aMimeTypeSeq;
116 sal_Bool SAL_CALL
117 QtTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
119 const auto aSeq = getTransferDataFlavors();
120 return std::any_of(aSeq.begin(), aSeq.end(), [&](const css::datatransfer::DataFlavor& aFlavor) {
121 return rFlavor.MimeType == aFlavor.MimeType;
125 css::uno::Any SAL_CALL QtTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
127 css::uno::Any aAny;
128 if (!isDataFlavorSupported(rFlavor))
129 return aAny;
131 if (rFlavor.MimeType == "text/plain;charset=utf-16")
133 OUString aString;
134 // use existing UTF-16 encoded text/plain or convert to UTF-16 as needed
135 if (m_pMimeData->hasFormat("text/plain;charset=utf-16"))
137 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
138 aString = OUString(reinterpret_cast<const sal_Unicode*>(aByteData.data()),
139 aByteData.size() / 2);
141 else if (m_pMimeData->hasFormat("text/plain;charset=utf-8"))
143 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain;charset=utf-8")));
144 aString = OUString::fromUtf8(reinterpret_cast<const char*>(aByteData.data()));
146 else
148 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain")));
149 aString = OUString(reinterpret_cast<const char*>(aByteData.data()), aByteData.size(),
150 osl_getThreadTextEncoding());
152 aAny <<= aString;
154 else
156 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
157 css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(aByteData.data()),
158 aByteData.size());
159 aAny <<= aSeq;
162 return aAny;
165 QtClipboardTransferable::QtClipboardTransferable(const QClipboard::Mode aMode,
166 const QMimeData* pMimeData)
167 : QtTransferable(pMimeData)
168 , m_aMode(aMode)
172 void QtClipboardTransferable::ensureConsistencyWithSystemClipboard()
174 const QMimeData* pCurrentClipboardData = QApplication::clipboard()->mimeData(m_aMode);
175 if (mimeData() != pCurrentClipboardData)
177 SAL_WARN("vcl.qt", "In flight clipboard change detected - updating mime data with current "
178 "clipboard contents.");
179 DBG_TESTSOLARMUTEX();
180 setMimeData(pCurrentClipboardData);
184 bool QtClipboardTransferable::hasMimeData(const QMimeData* pMimeData) const
186 SolarMutexGuard aGuard;
187 return QtTransferable::mimeData() == pMimeData;
190 css::uno::Any SAL_CALL
191 QtClipboardTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
193 css::uno::Any aAny;
194 SolarMutexGuard g;
195 GetQtInstance().RunInMainThread([&, this]() {
196 ensureConsistencyWithSystemClipboard();
197 aAny = QtTransferable::getTransferData(rFlavor);
199 return aAny;
202 css::uno::Sequence<css::datatransfer::DataFlavor>
203 SAL_CALL QtClipboardTransferable::getTransferDataFlavors()
205 css::uno::Sequence<css::datatransfer::DataFlavor> aSeq;
206 SolarMutexGuard g;
207 GetQtInstance().RunInMainThread([&, this]() {
208 ensureConsistencyWithSystemClipboard();
209 aSeq = QtTransferable::getTransferDataFlavors();
211 return aSeq;
214 sal_Bool SAL_CALL
215 QtClipboardTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
217 bool bIsSupported = false;
218 SolarMutexGuard g;
219 GetQtInstance().RunInMainThread([&, this]() {
220 ensureConsistencyWithSystemClipboard();
221 bIsSupported = QtTransferable::isDataFlavorSupported(rFlavor);
223 return bIsSupported;
226 QtMimeData::QtMimeData(const css::uno::Reference<css::datatransfer::XTransferable>& xTrans)
227 : m_aContents(xTrans)
228 , m_bHaveNoCharset(false)
229 , m_bHaveUTF8(false)
231 assert(xTrans.is());
234 bool QtMimeData::deepCopy(QMimeData** const pMimeCopy) const
236 if (!pMimeCopy)
237 return false;
239 QMimeData* pMimeData = new QMimeData();
240 for (QString& format : formats())
242 QByteArray aData = data(format);
243 // Checking for custom MIME types
244 if (format.startsWith("application/x-qt"))
246 // Retrieving true format name
247 int indexBegin = format.indexOf('"') + 1;
248 int indexEnd = format.indexOf('"', indexBegin);
249 format = format.mid(indexBegin, indexEnd - indexBegin);
251 pMimeData->setData(format, aData);
254 *pMimeCopy = pMimeData;
255 return true;
258 QStringList QtMimeData::formats() const
260 if (!m_aMimeTypeList.isEmpty())
261 return m_aMimeTypeList;
263 const css::uno::Sequence<css::datatransfer::DataFlavor> aFormats
264 = m_aContents->getTransferDataFlavors();
265 QStringList aList;
266 bool bHaveUTF16 = false;
268 for (const auto& rFlavor : aFormats)
270 aList << toQString(rFlavor.MimeType);
271 lcl_textMimeInfo(rFlavor.MimeType, m_bHaveNoCharset, bHaveUTF16, m_bHaveUTF8);
274 // we provide a locale encoded and a UTF-8 variant, if missing
275 if (m_bHaveNoCharset || bHaveUTF16 || m_bHaveUTF8)
277 // if there is a text representation from LO point of view, it'll be UTF-16
278 assert(bHaveUTF16);
279 if (!m_bHaveUTF8)
280 aList << QStringLiteral("text/plain;charset=utf-8");
281 if (!m_bHaveNoCharset)
282 aList << QStringLiteral("text/plain");
285 m_aMimeTypeList = aList;
286 return m_aMimeTypeList;
289 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
290 QVariant QtMimeData::retrieveData(const QString& mimeType, QVariant::Type) const
291 #else
292 QVariant QtMimeData::retrieveData(const QString& mimeType, QMetaType) const
293 #endif
295 if (!hasFormat(mimeType))
296 return QVariant();
298 css::datatransfer::DataFlavor aFlavor;
299 aFlavor.MimeType = toOUString(mimeType);
300 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
302 bool bWantNoCharset = false, bWantUTF16 = false, bWantUTF8 = false;
303 if (lcl_textMimeInfo(aFlavor.MimeType, bWantNoCharset, bWantUTF16, bWantUTF8))
305 if ((bWantNoCharset && !m_bHaveNoCharset) || (bWantUTF8 && !m_bHaveUTF8))
307 aFlavor.MimeType = "text/plain;charset=utf-16";
308 aFlavor.DataType = cppu::UnoType<OUString>::get();
310 else if (bWantUTF16)
311 aFlavor.DataType = cppu::UnoType<OUString>::get();
314 css::uno::Any aValue;
318 // tdf#129809 take a reference in case m_aContents is replaced during this call
319 css::uno::Reference<css::datatransfer::XTransferable> xCurrentContents(m_aContents);
320 aValue = xCurrentContents->getTransferData(aFlavor);
322 catch (...)
326 QByteArray aByteArray;
327 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING)
329 OUString aString;
330 aValue >>= aString;
332 if (bWantUTF8)
334 OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
335 aByteArray = QByteArray(aUTF8String.getStr(), aUTF8String.getLength());
337 else if (bWantNoCharset)
339 OString aLocaleString(OUStringToOString(aString, osl_getThreadTextEncoding()));
340 aByteArray = QByteArray(aLocaleString.getStr(), aLocaleString.getLength());
342 else if (bWantUTF16)
344 aByteArray = QByteArray(reinterpret_cast<const char*>(aString.getStr()),
345 aString.getLength() * 2);
347 else
348 return QVariant(toQString(aString));
350 else
352 css::uno::Sequence<sal_Int8> aData;
353 aValue >>= aData;
354 aByteArray
355 = QByteArray(reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength());
357 return QVariant::fromValue(aByteArray);
360 bool QtMimeData::hasFormat(const QString& mimeType) const { return formats().contains(mimeType); }
362 /* vim:set shiftwidth=4 softtabstop=4 expandtab cinoptions=b1,g0,N-s cinkeys+=0=break: */