bump product version to 6.4.0.3
[LibreOffice.git] / vcl / qt5 / Qt5Transferable.cxx
blob81a47871f4113154e053264842bb84c7aab243b6
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 <Qt5Transferable.hxx>
13 #include <comphelper/sequence.hxx>
14 #include <sal/log.hxx>
16 #include <QtWidgets/QApplication>
18 #include <Qt5Instance.hxx>
19 #include <Qt5Tools.hxx>
21 #include <cassert>
23 static bool lcl_textMimeInfo(const OUString& rMimeString, bool& bHaveNoCharset, bool& bHaveUTF16,
24 bool& bHaveUTF8)
26 sal_Int32 nIndex = 0;
27 if (rMimeString.getToken(0, ';', nIndex) == "text/plain")
29 OUString aToken(rMimeString.getToken(0, ';', nIndex));
30 if (aToken == "charset=utf-16")
31 bHaveUTF16 = true;
32 else if (aToken == "charset=utf-8")
33 bHaveUTF8 = true;
34 else if (aToken.isEmpty())
35 bHaveNoCharset = true;
36 else // we just handle UTF-16 and UTF-8, everything else is "bytes"
37 return false;
38 return true;
40 return false;
43 Qt5Transferable::Qt5Transferable(const QMimeData* pMimeData)
44 : m_pMimeData(pMimeData)
45 , m_bConvertFromLocale(false)
47 assert(pMimeData);
50 css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL Qt5Transferable::getTransferDataFlavors()
52 // it's just filled once, ever, so just try to get it without locking first
53 if (m_aMimeTypeSeq.hasElements())
54 return m_aMimeTypeSeq;
56 // better safe then sorry; preventing broken usage
57 // DnD should not be shared and Clipboard access runs in the GUI thread
58 osl::MutexGuard aGuard(m_aMutex);
59 if (m_aMimeTypeSeq.hasElements())
60 return m_aMimeTypeSeq;
62 QStringList aFormatList(m_pMimeData->formats());
63 // we might add the UTF-16 mime text variant later
64 const int nMimeTypeSeqSize = aFormatList.size() + 1;
65 bool bHaveNoCharset = false, bHaveUTF16 = false;
66 css::uno::Sequence<css::datatransfer::DataFlavor> aMimeTypeSeq(nMimeTypeSeqSize);
68 css::datatransfer::DataFlavor aFlavor;
69 int nMimeTypeCount = 0;
71 for (const QString& rMimeType : aFormatList)
73 // filter out non-MIME types such as TARGETS, MULTIPLE, TIMESTAMP
74 if (rMimeType.indexOf('/') == -1)
75 continue;
77 // gtk3 thinks it is not well defined - skip too
78 if (rMimeType == QStringLiteral("text/plain;charset=unicode"))
79 continue;
81 // LO doesn't like 'text/plain', so we have to provide UTF-16
82 bool bIsNoCharset = false, bIsUTF16 = false, bIsUTF8 = false;
83 if (lcl_textMimeInfo(toOUString(rMimeType), bIsNoCharset, bIsUTF16, bIsUTF8))
85 bHaveNoCharset |= bIsNoCharset;
86 bHaveUTF16 |= bIsUTF16;
87 if (bIsUTF16)
88 aFlavor.DataType = cppu::UnoType<OUString>::get();
89 else
90 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
92 else
93 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
95 aFlavor.MimeType = toOUString(rMimeType);
96 assert(nMimeTypeCount < nMimeTypeSeqSize);
97 aMimeTypeSeq[nMimeTypeCount] = aFlavor;
98 nMimeTypeCount++;
101 m_bConvertFromLocale = bHaveNoCharset && !bHaveUTF16;
102 if (m_bConvertFromLocale)
104 aFlavor.MimeType = "text/plain;charset=utf-16";
105 aFlavor.DataType = cppu::UnoType<OUString>::get();
106 assert(nMimeTypeCount < nMimeTypeSeqSize);
107 aMimeTypeSeq[nMimeTypeCount] = aFlavor;
108 nMimeTypeCount++;
111 aMimeTypeSeq.realloc(nMimeTypeCount);
113 m_aMimeTypeSeq = aMimeTypeSeq;
114 return m_aMimeTypeSeq;
117 sal_Bool SAL_CALL
118 Qt5Transferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
120 const auto aSeq = getTransferDataFlavors();
121 return std::any_of(aSeq.begin(), aSeq.end(), [&](const css::datatransfer::DataFlavor& aFlavor) {
122 return rFlavor.MimeType == aFlavor.MimeType;
126 css::uno::Any SAL_CALL
127 Qt5Transferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
129 css::uno::Any aAny;
130 if (!isDataFlavorSupported(rFlavor))
131 return aAny;
133 if (rFlavor.MimeType == "text/plain;charset=utf-16")
135 OUString aString;
136 if (m_bConvertFromLocale)
138 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain")));
139 aString = OUString(reinterpret_cast<const sal_Char*>(aByteData.data()),
140 aByteData.size(), osl_getThreadTextEncoding());
142 else
144 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
145 aString = OUString(reinterpret_cast<const sal_Unicode*>(aByteData.data()),
146 aByteData.size() / 2);
148 aAny <<= aString;
150 else
152 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
153 css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(aByteData.data()),
154 aByteData.size());
155 aAny <<= aSeq;
158 return aAny;
161 Qt5ClipboardTransferable::Qt5ClipboardTransferable(const QClipboard::Mode aMode,
162 const QMimeData* pMimeData)
163 : Qt5Transferable(pMimeData)
164 , m_aMode(aMode)
168 bool Qt5ClipboardTransferable::hasInFlightChanged() const
170 const bool bChanged(mimeData() != QApplication::clipboard()->mimeData(m_aMode));
171 SAL_WARN_IF(bChanged, "vcl.qt5",
172 "In flight clipboard change detected - broken clipboard read!");
173 return bChanged;
176 css::uno::Any SAL_CALL
177 Qt5ClipboardTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
179 css::uno::Any aAny;
180 auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
181 SolarMutexGuard g;
182 pSalInst->RunInMainThread([&, this]() {
183 if (!hasInFlightChanged())
184 aAny = Qt5Transferable::getTransferData(rFlavor);
186 return aAny;
189 css::uno::Sequence<css::datatransfer::DataFlavor>
190 SAL_CALL Qt5ClipboardTransferable::getTransferDataFlavors()
192 css::uno::Sequence<css::datatransfer::DataFlavor> aSeq;
193 auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
194 SolarMutexGuard g;
195 pSalInst->RunInMainThread([&, this]() {
196 if (!hasInFlightChanged())
197 aSeq = Qt5Transferable::getTransferDataFlavors();
199 return aSeq;
202 sal_Bool SAL_CALL
203 Qt5ClipboardTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
205 bool bIsSupported = false;
206 auto* pSalInst(static_cast<Qt5Instance*>(GetSalData()->m_pInstance));
207 SolarMutexGuard g;
208 pSalInst->RunInMainThread([&, this]() {
209 if (!hasInFlightChanged())
210 bIsSupported = Qt5Transferable::isDataFlavorSupported(rFlavor);
212 return bIsSupported;
215 Qt5MimeData::Qt5MimeData(const css::uno::Reference<css::datatransfer::XTransferable>& xTrans)
216 : m_aContents(xTrans)
217 , m_bHaveNoCharset(false)
218 , m_bHaveUTF8(false)
220 assert(xTrans.is());
223 bool Qt5MimeData::deepCopy(QMimeData** const pMimeCopy) const
225 if (!pMimeCopy)
226 return false;
228 QMimeData* pMimeData = new QMimeData();
229 for (QString& format : formats())
231 QByteArray aData = data(format);
232 // Checking for custom MIME types
233 if (format.startsWith("application/x-qt"))
235 // Retrieving true format name
236 int indexBegin = format.indexOf('"') + 1;
237 int indexEnd = format.indexOf('"', indexBegin);
238 format = format.mid(indexBegin, indexEnd - indexBegin);
240 pMimeData->setData(format, aData);
243 *pMimeCopy = pMimeData;
244 return true;
247 QStringList Qt5MimeData::formats() const
249 if (!m_aMimeTypeList.isEmpty())
250 return m_aMimeTypeList;
252 const css::uno::Sequence<css::datatransfer::DataFlavor> aFormats
253 = m_aContents->getTransferDataFlavors();
254 QStringList aList;
255 bool bHaveUTF16 = false;
257 for (const auto& rFlavor : aFormats)
259 aList << toQString(rFlavor.MimeType);
260 lcl_textMimeInfo(rFlavor.MimeType, m_bHaveNoCharset, bHaveUTF16, m_bHaveUTF8);
263 // we provide a locale encoded and a UTF-8 variant, if missing
264 if (m_bHaveNoCharset || bHaveUTF16 || m_bHaveUTF8)
266 // if there is a text representation from LO point of view, it'll be UTF-16
267 assert(bHaveUTF16);
268 if (!m_bHaveUTF8)
269 aList << QStringLiteral("text/plain;charset=utf-8");
270 if (!m_bHaveNoCharset)
271 aList << QStringLiteral("text/plain");
274 m_aMimeTypeList = aList;
275 return m_aMimeTypeList;
278 QVariant Qt5MimeData::retrieveData(const QString& mimeType, QVariant::Type) const
280 if (!hasFormat(mimeType))
281 return QVariant();
283 css::datatransfer::DataFlavor aFlavor;
284 aFlavor.MimeType = toOUString(mimeType);
285 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
287 bool bWantNoCharset = false, bWantUTF16 = false, bWantUTF8 = false;
288 if (lcl_textMimeInfo(aFlavor.MimeType, bWantNoCharset, bWantUTF16, bWantUTF8))
290 if ((bWantNoCharset && !m_bHaveNoCharset) || (bWantUTF8 && !m_bHaveUTF8))
292 aFlavor.MimeType = "text/plain;charset=utf-16";
293 aFlavor.DataType = cppu::UnoType<OUString>::get();
295 else if (bWantUTF16)
296 aFlavor.DataType = cppu::UnoType<OUString>::get();
299 css::uno::Any aValue;
303 aValue = m_aContents->getTransferData(aFlavor);
305 catch (...)
309 QByteArray aByteArray;
310 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING)
312 OUString aString;
313 aValue >>= aString;
315 if (bWantUTF8)
317 OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
318 aByteArray = QByteArray(reinterpret_cast<const char*>(aUTF8String.getStr()),
319 aUTF8String.getLength());
321 else if (bWantNoCharset)
323 OString aLocaleString(OUStringToOString(aString, osl_getThreadTextEncoding()));
324 aByteArray = QByteArray(reinterpret_cast<const char*>(aLocaleString.getStr()),
325 aLocaleString.getLength());
327 else
328 return QVariant(toQString(aString));
330 else
332 css::uno::Sequence<sal_Int8> aData;
333 aValue >>= aData;
334 aByteArray
335 = QByteArray(reinterpret_cast<const char*>(aData.getConstArray()), aData.getLength());
337 return QVariant::fromValue(aByteArray);
340 bool Qt5MimeData::hasFormat(const QString& mimeType) const { return formats().contains(mimeType); }
342 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */