Qt
Internal/Contributor docs for the Qt SDK. <b>Note:</b> These are NOT official API docs; those are found <a href='https://doc.qt.io/'>here</a>.
Loading...
Searching...
No Matches
qlocalfileapi.cpp
Go to the documentation of this file.
1// Copyright (C) 2022 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qlocalfileapi_p.h"
5#include <private/qstdweb_p.h>
6#include <QtCore/QRegularExpression>
7
9namespace LocalFileApi {
10namespace {
11std::string qtFilterListToFileInputAccept(const QStringList &filterList)
12{
13 QStringList transformed;
14 for (const auto &filter : filterList) {
15 const auto type = Type::fromQt(filter);
16 if (type && type->accept()) {
17 const auto &extensions = type->accept()->mimeType().extensions();
18 std::transform(extensions.begin(), extensions.end(), std::back_inserter(transformed),
19 [](const Type::Accept::MimeType::Extension &extension) {
20 return extension.value().toString();
21 });
22 }
23 }
24 return transformed.join(QStringLiteral(",")).toStdString();
25}
26
27std::optional<emscripten::val> qtFilterListToTypes(const QStringList &filterList)
28{
29 using namespace qstdweb;
30 using namespace emscripten;
31 auto types = emscripten::val::array();
32
33 for (const auto &fileFilter : filterList) {
34 auto type = Type::fromQt(fileFilter);
35 if (type) {
36 auto jsType = emscripten::val::object();
37 jsType.set("description", type->description().toString().toStdString());
38 if (type->accept()) {
39 jsType.set("accept", ([&mimeType = type->accept()->mimeType()]() {
40 val acceptDict = val::object();
41
42 QList<emscripten::val> extensions;
43 extensions.reserve(mimeType.extensions().size());
44 std::transform(
45 mimeType.extensions().begin(), mimeType.extensions().end(),
46 std::back_inserter(extensions),
47 [](const Type::Accept::MimeType::Extension &extension) {
48 return val(extension.value().toString().toStdString());
49 });
50 acceptDict.set("application/octet-stream",
51 emscripten::val::array(extensions.begin(),
52 extensions.end()));
53 return acceptDict;
54 })());
55 }
56 types.call<void>("push", std::move(jsType));
57 }
58 }
59
60 return types["length"].as<int>() == 0 ? std::optional<emscripten::val>() : types;
61}
62} // namespace
63
64Type::Type(QStringView description, std::optional<Accept> accept)
65 : m_description(description.trimmed()), m_accept(std::move(accept))
66{
67}
68
69Type::~Type() = default;
70
71std::optional<Type> Type::fromQt(QStringView type)
72{
73 using namespace emscripten;
74
75 // Accepts either a string in format:
76 // GROUP3
77 // or in this format:
78 // GROUP1 (GROUP2)
79 // Group 1 is treated as the description, whereas group 2 or 3 are treated as the filter list.
80 static QRegularExpression regex(
81 QString(QStringLiteral("(?:(?:([^(]*)\\(([^()]+)\\)[^)]*)|([^()]+))")));
82 const auto match = regex.matchView(type);
83
84 if (!match.hasMatch())
85 return std::nullopt;
86
87 constexpr size_t DescriptionIndex = 1;
88 constexpr size_t FilterListFromParensIndex = 2;
89 constexpr size_t PlainFilterListIndex = 3;
90
91 const auto description = match.hasCaptured(DescriptionIndex)
92 ? match.capturedView(DescriptionIndex)
93 : QStringView();
94 const auto filterList = match.capturedView(match.hasCaptured(FilterListFromParensIndex)
95 ? FilterListFromParensIndex
96 : PlainFilterListIndex);
97
98 auto accept = Type::Accept::fromQt(filterList);
99 if (!accept)
100 return std::nullopt;
101
102 return Type(description, std::move(*accept));
103}
104
105Type::Accept::Accept() = default;
106
107Type::Accept::~Accept() = default;
108
109std::optional<Type::Accept> Type::Accept::fromQt(QStringView qtRepresentation)
110{
111 Accept accept;
112
113 // Used for accepting multiple extension specifications on a filter list.
114 // The next group of non-empty characters.
115 static QRegularExpression internalRegex(QString(QStringLiteral("([^\\s]+)\\s*")));
116 int offset = 0;
117 auto internalMatch = internalRegex.matchView(qtRepresentation, offset);
119
120 while (internalMatch.hasMatch()) {
121 auto webExtension = MimeType::Extension::fromQt(internalMatch.capturedView(1));
122
123 if (!webExtension)
124 return std::nullopt;
125
126 mimeType.addExtension(*webExtension);
127
128 internalMatch = internalRegex.matchView(qtRepresentation, internalMatch.capturedEnd());
129 }
130
131 accept.setMimeType(mimeType);
132 return accept;
133}
134
135void Type::Accept::setMimeType(MimeType mimeType)
136{
137 m_mimeType = std::move(mimeType);
138}
139
140Type::Accept::MimeType::MimeType() = default;
141
142Type::Accept::MimeType::~MimeType() = default;
143
144void Type::Accept::MimeType::addExtension(Extension extension)
145{
146 m_extensions.push_back(std::move(extension));
147}
148
149Type::Accept::MimeType::Extension::Extension(QStringView extension) : m_value(extension) { }
150
151Type::Accept::MimeType::Extension::~Extension() = default;
152
153std::optional<Type::Accept::MimeType::Extension>
154Type::Accept::MimeType::Extension::fromQt(QStringView qtRepresentation)
155{
156 // Checks for a filter that matches everything:
157 // Any number of asterisks or any number of asterisks with a '.' between them.
158 // The web filter does not support wildcards.
159 static QRegularExpression qtAcceptAllRegex(
161 if (qtAcceptAllRegex.matchView(qtRepresentation).hasMatch())
162 return std::nullopt;
163
164 // Checks for correctness. The web filter only allows filename extensions and does not filter
165 // the actual filenames, therefore we check whether the filter provided only filters for the
166 // extension.
167 static QRegularExpression qtFilenameMatcherRegex(
169
170 auto extensionMatch = qtFilenameMatcherRegex.matchView(qtRepresentation);
171 if (extensionMatch.hasMatch())
172 return Extension(extensionMatch.capturedView(2));
173
174 // Mapping impossible.
175 return std::nullopt;
176}
177
178emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple)
179{
180 auto options = emscripten::val::object();
181 if (auto typeList = qtFilterListToTypes(filterList); typeList) {
182 options.set("types", std::move(*typeList));
183 options.set("excludeAcceptAllOption", true);
184 }
185
186 options.set("multiple", acceptMultiple);
187
188 return options;
189}
190
191emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::string& suggestedName)
192{
193 auto options = emscripten::val::object();
194
195 if (!suggestedName.empty())
196 options.set("suggestedName", emscripten::val(suggestedName));
197
198 if (auto typeList = qtFilterListToTypes(filterList))
199 options.set("types", emscripten::val(std::move(*typeList)));
200
201 return options;
202}
203
204std::string makeFileInputAccept(const QStringList &filterList)
205{
206 return qtFilterListToFileInputAccept(filterList);
207}
208
209} // namespace LocalFileApi
210
NSString * m_mimeType
void setMimeType(MimeType mimeType)
const QStringView & description() const
const std::optional< Accept > & accept() const
\inmodule QtCore \reentrant
static QString anchoredPattern(const QString &expression)
\inmodule QtCore
\inmodule QtCore
Definition qstringview.h:78
\macro QT_RESTRICTED_CAST_FROM_ASCII
Definition qstring.h:129
void extension()
[6]
Definition dialogs.cpp:230
std::string makeFileInputAccept(const QStringList &filterList)
emscripten::val makeOpenFileOptions(const QStringList &filterList, bool acceptMultiple)
emscripten::val makeSaveFileOptions(const QStringList &filterList, const std::string &suggestedName)
Combined button and popup list for selecting options.
const char * mimeType
GLsizei GLenum GLenum * types
GLenum type
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
GLenum GLuint GLintptr offset
static const char * typeList[]
#define QStringLiteral(str)
static bool match(const uchar *found, uint foundLen, const char *target, uint targetLen)