tdf#148168 set built-in gtk search box to search "document name" column
[LibreOffice.git] / vcl / qt5 / QtTransferable.cxx
blob24073c9fd792b1ce0b26818db33701b28143852d
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>
16 #include <QtWidgets/QApplication>
18 #include <QtInstance.hxx>
19 #include <QtTools.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 QtTransferable::QtTransferable(const QMimeData* pMimeData)
44 : m_pMimeData(pMimeData)
45 , m_bProvideUTF16FromOtherEncoding(false)
47 assert(pMimeData);
50 css::uno::Sequence<css::datatransfer::DataFlavor> SAL_CALL QtTransferable::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, bHaveUTF8 = false;
66 css::uno::Sequence<css::datatransfer::DataFlavor> aMimeTypeSeq(nMimeTypeSeqSize);
67 auto pMimeTypeSeq = aMimeTypeSeq.getArray();
69 css::datatransfer::DataFlavor aFlavor;
70 int nMimeTypeCount = 0;
72 for (const QString& rMimeType : aFormatList)
74 // filter out non-MIME types such as TARGETS, MULTIPLE, TIMESTAMP
75 if (rMimeType.indexOf('/') == -1)
76 continue;
78 // gtk3 thinks it is not well defined - skip too
79 if (rMimeType == QStringLiteral("text/plain;charset=unicode"))
80 continue;
82 // LO doesn't like 'text/plain', so we have to provide UTF-16
83 bool bIsNoCharset = false, bIsUTF16 = false, bIsUTF8 = false;
84 if (lcl_textMimeInfo(toOUString(rMimeType), bIsNoCharset, bIsUTF16, bIsUTF8))
86 bHaveNoCharset |= bIsNoCharset;
87 bHaveUTF16 |= bIsUTF16;
88 bHaveUTF8 |= bIsUTF8;
89 if (bIsUTF16)
90 aFlavor.DataType = cppu::UnoType<OUString>::get();
91 else
92 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
94 else
95 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
97 aFlavor.MimeType = toOUString(rMimeType);
98 assert(nMimeTypeCount < nMimeTypeSeqSize);
99 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
100 nMimeTypeCount++;
103 m_bProvideUTF16FromOtherEncoding = (bHaveNoCharset || bHaveUTF8) && !bHaveUTF16;
104 if (m_bProvideUTF16FromOtherEncoding)
106 aFlavor.MimeType = "text/plain;charset=utf-16";
107 aFlavor.DataType = cppu::UnoType<OUString>::get();
108 assert(nMimeTypeCount < nMimeTypeSeqSize);
109 pMimeTypeSeq[nMimeTypeCount] = aFlavor;
110 nMimeTypeCount++;
113 aMimeTypeSeq.realloc(nMimeTypeCount);
115 m_aMimeTypeSeq = aMimeTypeSeq;
116 return m_aMimeTypeSeq;
119 sal_Bool SAL_CALL
120 QtTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
122 const auto aSeq = getTransferDataFlavors();
123 return std::any_of(aSeq.begin(), aSeq.end(), [&](const css::datatransfer::DataFlavor& aFlavor) {
124 return rFlavor.MimeType == aFlavor.MimeType;
128 css::uno::Any SAL_CALL QtTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
130 css::uno::Any aAny;
131 if (!isDataFlavorSupported(rFlavor))
132 return aAny;
134 if (rFlavor.MimeType == "text/plain;charset=utf-16")
136 OUString aString;
137 if (m_bProvideUTF16FromOtherEncoding)
139 if (m_pMimeData->hasFormat("text/plain;charset=utf-8"))
141 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain;charset=utf-8")));
142 aString = OUString::fromUtf8(reinterpret_cast<const char*>(aByteData.data()));
144 else
146 QByteArray aByteData(m_pMimeData->data(QStringLiteral("text/plain")));
147 aString = OUString(reinterpret_cast<const char*>(aByteData.data()),
148 aByteData.size(), osl_getThreadTextEncoding());
151 else
153 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
154 aString = OUString(reinterpret_cast<const sal_Unicode*>(aByteData.data()),
155 aByteData.size() / 2);
157 aAny <<= aString;
159 else
161 QByteArray aByteData(m_pMimeData->data(toQString(rFlavor.MimeType)));
162 css::uno::Sequence<sal_Int8> aSeq(reinterpret_cast<const sal_Int8*>(aByteData.data()),
163 aByteData.size());
164 aAny <<= aSeq;
167 return aAny;
170 QtClipboardTransferable::QtClipboardTransferable(const QClipboard::Mode aMode,
171 const QMimeData* pMimeData)
172 : QtTransferable(pMimeData)
173 , m_aMode(aMode)
177 bool QtClipboardTransferable::hasInFlightChanged() const
179 const bool bChanged(mimeData() != QApplication::clipboard()->mimeData(m_aMode));
180 SAL_WARN_IF(bChanged, "vcl.qt", "In flight clipboard change detected - broken clipboard read!");
181 return bChanged;
184 css::uno::Any SAL_CALL
185 QtClipboardTransferable::getTransferData(const css::datatransfer::DataFlavor& rFlavor)
187 css::uno::Any aAny;
188 auto* pSalInst(static_cast<QtInstance*>(GetSalData()->m_pInstance));
189 SolarMutexGuard g;
190 pSalInst->RunInMainThread([&, this]() {
191 if (!hasInFlightChanged())
192 aAny = QtTransferable::getTransferData(rFlavor);
194 return aAny;
197 css::uno::Sequence<css::datatransfer::DataFlavor>
198 SAL_CALL QtClipboardTransferable::getTransferDataFlavors()
200 css::uno::Sequence<css::datatransfer::DataFlavor> aSeq;
201 auto* pSalInst(static_cast<QtInstance*>(GetSalData()->m_pInstance));
202 SolarMutexGuard g;
203 pSalInst->RunInMainThread([&, this]() {
204 if (!hasInFlightChanged())
205 aSeq = QtTransferable::getTransferDataFlavors();
207 return aSeq;
210 sal_Bool SAL_CALL
211 QtClipboardTransferable::isDataFlavorSupported(const css::datatransfer::DataFlavor& rFlavor)
213 bool bIsSupported = false;
214 auto* pSalInst(static_cast<QtInstance*>(GetSalData()->m_pInstance));
215 SolarMutexGuard g;
216 pSalInst->RunInMainThread([&, this]() {
217 if (!hasInFlightChanged())
218 bIsSupported = QtTransferable::isDataFlavorSupported(rFlavor);
220 return bIsSupported;
223 QtMimeData::QtMimeData(const css::uno::Reference<css::datatransfer::XTransferable>& xTrans)
224 : m_aContents(xTrans)
225 , m_bHaveNoCharset(false)
226 , m_bHaveUTF8(false)
228 assert(xTrans.is());
231 bool QtMimeData::deepCopy(QMimeData** const pMimeCopy) const
233 if (!pMimeCopy)
234 return false;
236 QMimeData* pMimeData = new QMimeData();
237 for (QString& format : formats())
239 QByteArray aData = data(format);
240 // Checking for custom MIME types
241 if (format.startsWith("application/x-qt"))
243 // Retrieving true format name
244 int indexBegin = format.indexOf('"') + 1;
245 int indexEnd = format.indexOf('"', indexBegin);
246 format = format.mid(indexBegin, indexEnd - indexBegin);
248 pMimeData->setData(format, aData);
251 *pMimeCopy = pMimeData;
252 return true;
255 QStringList QtMimeData::formats() const
257 if (!m_aMimeTypeList.isEmpty())
258 return m_aMimeTypeList;
260 const css::uno::Sequence<css::datatransfer::DataFlavor> aFormats
261 = m_aContents->getTransferDataFlavors();
262 QStringList aList;
263 bool bHaveUTF16 = false;
265 for (const auto& rFlavor : aFormats)
267 aList << toQString(rFlavor.MimeType);
268 lcl_textMimeInfo(rFlavor.MimeType, m_bHaveNoCharset, bHaveUTF16, m_bHaveUTF8);
271 // we provide a locale encoded and a UTF-8 variant, if missing
272 if (m_bHaveNoCharset || bHaveUTF16 || m_bHaveUTF8)
274 // if there is a text representation from LO point of view, it'll be UTF-16
275 assert(bHaveUTF16);
276 if (!m_bHaveUTF8)
277 aList << QStringLiteral("text/plain;charset=utf-8");
278 if (!m_bHaveNoCharset)
279 aList << QStringLiteral("text/plain");
282 m_aMimeTypeList = aList;
283 return m_aMimeTypeList;
286 #if QT_VERSION < QT_VERSION_CHECK(6, 0, 0)
287 QVariant QtMimeData::retrieveData(const QString& mimeType, QVariant::Type) const
288 #else
289 QVariant QtMimeData::retrieveData(const QString& mimeType, QMetaType) const
290 #endif
292 if (!hasFormat(mimeType))
293 return QVariant();
295 css::datatransfer::DataFlavor aFlavor;
296 aFlavor.MimeType = toOUString(mimeType);
297 aFlavor.DataType = cppu::UnoType<css::uno::Sequence<sal_Int8>>::get();
299 bool bWantNoCharset = false, bWantUTF16 = false, bWantUTF8 = false;
300 if (lcl_textMimeInfo(aFlavor.MimeType, bWantNoCharset, bWantUTF16, bWantUTF8))
302 if ((bWantNoCharset && !m_bHaveNoCharset) || (bWantUTF8 && !m_bHaveUTF8))
304 aFlavor.MimeType = "text/plain;charset=utf-16";
305 aFlavor.DataType = cppu::UnoType<OUString>::get();
307 else if (bWantUTF16)
308 aFlavor.DataType = cppu::UnoType<OUString>::get();
311 css::uno::Any aValue;
315 // tdf#129809 take a reference in case m_aContents is replaced during this call
316 css::uno::Reference<com::sun::star::datatransfer::XTransferable> xCurrentContents(
317 m_aContents);
318 aValue = xCurrentContents->getTransferData(aFlavor);
320 catch (...)
324 QByteArray aByteArray;
325 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING)
327 OUString aString;
328 aValue >>= aString;
330 if (bWantUTF8)
332 OString aUTF8String(OUStringToOString(aString, RTL_TEXTENCODING_UTF8));
333 aByteArray = QByteArray(reinterpret_cast<const char*>(aUTF8String.getStr()),
334 aUTF8String.getLength());
336 else if (bWantNoCharset)
338 OString aLocaleString(OUStringToOString(aString, osl_getThreadTextEncoding()));
339 aByteArray = QByteArray(reinterpret_cast<const char*>(aLocaleString.getStr()),
340 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: */