1// Copyright (C) 2016 The Qt Company Ltd.
2// Copyright (C) 2015 Klaralvdalens Datakonsult AB, a KDAB Group company, info@kdab.com, author David Faure <david.faure@kdab.com>
3// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
4
5#include "qmimetype.h"
6
7#include "qmimetype_p.h"
8#include "qmimedatabase_p.h"
9#include "qmimeprovider_p.h"
10
11#include "qmimeglobpattern_p.h"
12
13#include <QtCore/QDebug>
14#include <QtCore/QLocale>
15#include <QtCore/QHashFunctions>
16
17#include <memory>
18
19QT_BEGIN_NAMESPACE
20
21using namespace Qt::StringLiterals;
22
23QMimeTypePrivate::QMimeTypePrivate()
24 : loaded(false), fromCache(false)
25{}
26
27QMimeTypePrivate::QMimeTypePrivate(const QMimeType &other)
28 : loaded(other.d->loaded),
29 name(other.d->name),
30 localeComments(other.d->localeComments),
31 genericIconName(other.d->genericIconName),
32 iconName(other.d->iconName),
33 globPatterns(other.d->globPatterns)
34{}
35
36void QMimeTypePrivate::clear()
37{
38 name.clear();
39 localeComments.clear();
40 genericIconName.clear();
41 iconName.clear();
42 globPatterns.clear();
43}
44
45void QMimeTypePrivate::addGlobPattern(const QString &pattern)
46{
47 globPatterns.append(t: pattern);
48}
49
50/*!
51 \class QMimeType
52 \inmodule QtCore
53 \ingroup shared
54 \brief The QMimeType class describes types of file or data, represented by a MIME type string.
55
56 \since 5.0
57
58 For instance a file named "readme.txt" has the MIME type "text/plain".
59 The MIME type can be determined from the file name, or from the file
60 contents, or from both. MIME type determination can also be done on
61 buffers of data not coming from files.
62
63 Determining the MIME type of a file can be useful to make sure your
64 application supports it. It is also useful in file-manager-like applications
65 or widgets, in order to display an appropriate \l {QMimeType::iconName}{icon} for the file, or even
66 the descriptive \l {QMimeType::comment()}{comment} in detailed views.
67
68 To check if a file has the expected MIME type, you should use inherits()
69 rather than a simple string comparison based on the name(). This is because
70 MIME types can inherit from each other: for instance a C source file is
71 a specific type of plain text file, so text/x-csrc inherits text/plain.
72
73 \sa QMimeDatabase, {MIME Type Browser Example}
74 */
75
76/*!
77 \fn QMimeType &QMimeType::operator=(QMimeType &&other)
78
79 Move-assigns \a other to this QMimeType instance.
80
81 \since 5.2
82*/
83
84/*!
85 \fn QMimeType::QMimeType();
86 Constructs this QMimeType object initialized with default property values that indicate an invalid MIME type.
87 */
88QMimeType::QMimeType() :
89 d(new QMimeTypePrivate())
90{
91}
92
93/*!
94 \fn QMimeType::QMimeType(const QMimeType &other);
95 Constructs this QMimeType object as a copy of \a other.
96 */
97QMimeType::QMimeType(const QMimeType &other) :
98 d(other.d)
99{
100}
101
102/*!
103 \fn QMimeType &QMimeType::operator=(const QMimeType &other);
104 Assigns the data of \a other to this QMimeType object, and returns a reference to this object.
105 */
106QMimeType &QMimeType::operator=(const QMimeType &other)
107{
108 if (d != other.d)
109 d = other.d;
110 return *this;
111}
112
113/*!
114 \fn QMimeType::QMimeType(const QMimeTypePrivate &dd);
115 Assigns the data of the QMimeTypePrivate \a dd to this QMimeType object, and returns a reference to this object.
116 \internal
117 */
118QMimeType::QMimeType(const QMimeTypePrivate &dd) :
119 d(new QMimeTypePrivate(dd))
120{
121}
122
123/*!
124 \fn void QMimeType::swap(QMimeType &other);
125 Swaps QMimeType \a other with this QMimeType object.
126
127 This operation is very fast and never fails.
128
129 The swap() method helps with the implementation of assignment
130 operators in an exception-safe way. For more information consult
131 \l {http://en.wikibooks.org/wiki/More_C++_Idioms/Copy-and-swap}
132 {More C++ Idioms - Copy-and-swap}.
133 */
134
135/*!
136 \fn QMimeType::~QMimeType();
137 Destroys the QMimeType object, and releases the d pointer.
138 */
139QMimeType::~QMimeType()
140{
141}
142
143/*!
144 \fn bool QMimeType::operator==(const QMimeType &other) const;
145 Returns \c true if \a other equals this QMimeType object, otherwise returns \c false.
146 The name is the unique identifier for a mimetype, so two mimetypes with
147 the same name, are equal.
148 */
149bool QMimeType::operator==(const QMimeType &other) const
150{
151 return d == other.d || d->name == other.d->name;
152}
153
154/*!
155 \since 5.6
156 \relates QMimeType
157
158 Returns the hash value for \a key, using
159 \a seed to seed the calculation.
160 */
161size_t qHash(const QMimeType &key, size_t seed) noexcept
162{
163 return qHash(key: key.d->name, seed);
164}
165
166/*!
167 \fn bool QMimeType::operator!=(const QMimeType &other) const;
168 Returns \c true if \a other does not equal this QMimeType object, otherwise returns \c false.
169 */
170
171/*!
172 \property QMimeType::valid
173 \brief \c true if the QMimeType object contains valid data, \c false otherwise
174
175 A valid MIME type has a non-empty name().
176 The invalid MIME type is the default-constructed QMimeType.
177
178 While this property was introduced in 5.10, the
179 corresponding accessor method has always been there.
180 */
181bool QMimeType::isValid() const
182{
183 return !d->name.isEmpty();
184}
185
186/*!
187 \property QMimeType::isDefault
188 \brief \c true if this MIME type is the default MIME type which
189 applies to all files: application/octet-stream.
190
191 While this property was introduced in 5.10, the
192 corresponding accessor method has always been there.
193 */
194bool QMimeType::isDefault() const
195{
196 return d->name == QMimeDatabasePrivate::instance()->defaultMimeType();
197}
198
199/*!
200 \property QMimeType::name
201 \brief the name of the MIME type
202
203 While this property was introduced in 5.10, the
204 corresponding accessor method has always been there.
205 */
206QString QMimeType::name() const
207{
208 return d->name;
209}
210
211/*!
212 \property QMimeType::comment
213 \brief the description of the MIME type to be displayed on user interfaces
214
215 The default language (QLocale().name()) is used to select the appropriate translation.
216
217 While this property was introduced in 5.10, the
218 corresponding accessor method has always been there.
219 */
220QString QMimeType::comment() const
221{
222 QMimeDatabasePrivate::instance()->loadMimeTypePrivate(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
223
224 QStringList languageList = QLocale().uiLanguages();
225 qsizetype defaultIndex = languageList.indexOf(str: u"en-US"_s);
226
227 // Include the default locale as fall-back.
228 if (defaultIndex >= 0) {
229 // en_US is generally the default, and may be omitted from the
230 // overtly-named locales in the MIME type's data (QTBUG-105007).
231 ++defaultIndex; // Skip over en-US.
232 // That's typically followed by en-Latn-US and en (in that order):
233 if (defaultIndex < languageList.size() && languageList.at(i: defaultIndex) == u"en-Latn-US")
234 ++defaultIndex;
235 if (defaultIndex < languageList.size() && languageList.at(i: defaultIndex) == u"en")
236 ++defaultIndex;
237 } else {
238 // Absent en-US, just append it:
239 defaultIndex = languageList.size();
240 }
241 languageList.insert(i: defaultIndex, t: u"default"_s);
242
243 for (const QString &language : std::as_const(t&: languageList)) {
244 // uiLanguages() uses '-' as separator, MIME database uses '_'
245 const QString lang
246 = language == "C"_L1 ? u"en_US"_s : QString(language).replace(before: u'-', after: u'_');
247 QString comm = d->localeComments.value(key: lang);
248 if (!comm.isEmpty())
249 return comm;
250 const qsizetype cut = lang.indexOf(c: u'_');
251 // If "de_CH" is missing, check for "de" (and similar):
252 if (cut != -1) {
253 comm = d->localeComments.value(key: lang.left(n: cut));
254 if (!comm.isEmpty())
255 return comm;
256 }
257 }
258
259 // Use the mimetype name as fallback
260 return d->name;
261}
262
263/*!
264 \property QMimeType::genericIconName
265 \brief the file name of a generic icon that represents the MIME type
266
267 This should be used if the icon returned by iconName() cannot be found on
268 the system. It is used for categories of similar types (like spreadsheets
269 or archives) that can use a common icon.
270 The freedesktop.org Icon Naming Specification lists a set of such icon names.
271
272 The icon name can be given to QIcon::fromTheme() in order to load the icon.
273
274 While this property was introduced in 5.10, the
275 corresponding accessor method has always been there.
276 */
277QString QMimeType::genericIconName() const
278{
279 QMimeDatabasePrivate::instance()->loadGenericIcon(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
280 if (d->genericIconName.isEmpty()) {
281 // From the spec:
282 // If the generic icon name is empty (not specified by the mimetype definition)
283 // then the mimetype is used to generate the generic icon by using the top-level
284 // media type (e.g. "video" in "video/ogg") and appending "-x-generic"
285 // (i.e. "video-x-generic" in the previous example).
286 const QString group = name();
287 QStringView groupRef(group);
288 const qsizetype slashindex = groupRef.indexOf(c: u'/');
289 if (slashindex != -1)
290 groupRef = groupRef.left(n: slashindex);
291 return groupRef + "-x-generic"_L1;
292 }
293 return d->genericIconName;
294}
295
296static QString make_default_icon_name_from_mimetype_name(QString iconName)
297{
298 const qsizetype slashindex = iconName.indexOf(c: u'/');
299 if (slashindex != -1)
300 iconName[slashindex] = u'-';
301 return iconName;
302}
303
304/*!
305 \property QMimeType::iconName
306 \brief the file name of an icon image that represents the MIME type
307
308 The icon name can be given to QIcon::fromTheme() in order to load the icon.
309
310 While this property was introduced in 5.10, the
311 corresponding accessor method has always been there.
312 */
313QString QMimeType::iconName() const
314{
315 QMimeDatabasePrivate::instance()->loadIcon(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
316 if (d->iconName.isEmpty()) {
317 return make_default_icon_name_from_mimetype_name(iconName: name());
318 }
319 return d->iconName;
320}
321
322/*!
323 \property QMimeType::globPatterns
324 \brief the list of glob matching patterns
325
326 While this property was introduced in 5.10, the
327 corresponding accessor method has always been there.
328 */
329QStringList QMimeType::globPatterns() const
330{
331 QMimeDatabasePrivate::instance()->loadMimeTypePrivate(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
332 return d->globPatterns;
333}
334
335/*!
336 \property QMimeType::parentMimeTypes
337 \brief the names of parent MIME types
338
339 A type is a subclass of another type if any instance of the first type is
340 also an instance of the second. For example, all image/svg+xml files are also
341 text/xml, text/plain and application/octet-stream files. Subclassing is about
342 the format, rather than the category of the data (for example, there is no
343 'generic spreadsheet' class that all spreadsheets inherit from).
344 Conversely, the parent mimetype of image/svg+xml is text/xml.
345
346 A mimetype can have multiple parents. For instance application/x-perl
347 has two parents: application/x-executable and text/plain. This makes
348 it possible to both execute perl scripts, and to open them in text editors.
349
350 While this property was introduced in 5.10, the
351 corresponding accessor method has always been there.
352*/
353QStringList QMimeType::parentMimeTypes() const
354{
355 return QMimeDatabasePrivate::instance()->mimeParents(mimeName: d->name);
356}
357
358static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
359{
360 const QStringList parents = QMimeDatabasePrivate::instance()->mimeParents(mimeName: mime);
361 for (const QString &parent : parents) {
362 // I would use QSet, but since order matters I better not
363 if (!allParents.contains(str: parent))
364 allParents.append(t: parent);
365 }
366 // We want a breadth-first search, so that the least-specific parent (octet-stream) is last
367 // This means iterating twice, unfortunately.
368 for (const QString &parent : parents)
369 collectParentMimeTypes(mime: parent, allParents);
370}
371
372/*!
373 \property QMimeType::allAncestors
374 \brief the names of direct and indirect parent MIME types
375
376 Return all the parent mimetypes of this mimetype, direct and indirect.
377 This includes the parent(s) of its parent(s), etc.
378
379 For instance, for image/svg+xml the list would be:
380 application/xml, text/plain, application/octet-stream.
381
382 Note that application/octet-stream is the ultimate parent for all types
383 of files (but not directories).
384
385 While this property was introduced in 5.10, the
386 corresponding accessor method has always been there.
387*/
388QStringList QMimeType::allAncestors() const
389{
390 QStringList allParents;
391 collectParentMimeTypes(mime: d->name, allParents);
392 return allParents;
393}
394
395/*!
396 \property QMimeType::aliases
397 \brief the list of aliases of this mimetype
398
399 For instance, for text/csv, the returned list would be:
400 text/x-csv, text/x-comma-separated-values.
401
402 Note that all QMimeType instances refer to proper mimetypes,
403 never to aliases directly.
404
405 The order of the aliases in the list is undefined.
406
407 While this property was introduced in 5.10, the
408 corresponding accessor method has always been there.
409*/
410QStringList QMimeType::aliases() const
411{
412 return QMimeDatabasePrivate::instance()->listAliases(mimeName: d->name);
413}
414
415/*!
416 \property QMimeType::suffixes
417 \brief the known suffixes for the MIME type
418
419 No leading dot is included, so for instance this would return "jpg", "jpeg" for image/jpeg.
420
421 While this property was introduced in 5.10, the
422 corresponding accessor method has always been there.
423 */
424QStringList QMimeType::suffixes() const
425{
426 QMimeDatabasePrivate::instance()->loadMimeTypePrivate(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
427
428 QStringList result;
429 for (const QString &pattern : std::as_const(t&: d->globPatterns)) {
430 // Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP?
431 if (pattern.startsWith(s: "*."_L1) &&
432 pattern.size() > 2 &&
433 pattern.indexOf(c: u'*', from: 2) < 0 && pattern.indexOf(c: u'?', from: 2) < 0) {
434 const QString suffix = pattern.mid(position: 2);
435 result.append(t: suffix);
436 }
437 }
438
439 return result;
440}
441
442/*!
443 \property QMimeType::preferredSuffix
444 \brief the preferred suffix for the MIME type
445
446 No leading dot is included, so for instance this would return "pdf" for application/pdf.
447 The return value can be empty, for mime types which do not have any suffixes associated.
448
449 While this property was introduced in 5.10, the
450 corresponding accessor method has always been there.
451 */
452QString QMimeType::preferredSuffix() const
453{
454 if (isDefault()) // workaround for unwanted *.bin suffix for octet-stream, https://bugs.freedesktop.org/show_bug.cgi?id=101667, fixed upstream in 1.10
455 return QString();
456 const QStringList suffixList = suffixes();
457 return suffixList.isEmpty() ? QString() : suffixList.at(i: 0);
458}
459
460/*!
461 \property QMimeType::filterString
462 \brief a filter string usable for a file dialog
463
464 While this property was introduced in 5.10, the
465 corresponding accessor method has always been there.
466*/
467QString QMimeType::filterString() const
468{
469 QMimeDatabasePrivate::instance()->loadMimeTypePrivate(mimePrivate&: const_cast<QMimeTypePrivate&>(*d));
470 QString filter;
471
472 if (!d->globPatterns.empty()) {
473 filter += comment() + " ("_L1;
474 for (int i = 0; i < d->globPatterns.size(); ++i) {
475 if (i != 0)
476 filter += u' ';
477 filter += d->globPatterns.at(i);
478 }
479 filter += u')';
480 }
481
482 return filter;
483}
484
485/*!
486 \fn bool QMimeType::inherits(const QString &mimeTypeName) const;
487 Returns \c true if this mimetype is \a mimeTypeName,
488 or inherits \a mimeTypeName (see parentMimeTypes()),
489 or \a mimeTypeName is an alias for this mimetype.
490
491 This method has been made invokable from QML since 5.10.
492 */
493bool QMimeType::inherits(const QString &mimeTypeName) const
494{
495 if (d->name == mimeTypeName)
496 return true;
497 return QMimeDatabasePrivate::instance()->mimeInherits(mime: d->name, parent: mimeTypeName);
498}
499
500#ifndef QT_NO_DEBUG_STREAM
501QDebug operator<<(QDebug debug, const QMimeType &mime)
502{
503 QDebugStateSaver saver(debug);
504 if (!mime.isValid()) {
505 debug.nospace() << "QMimeType(invalid)";
506 } else {
507 debug.nospace() << "QMimeType(" << mime.name() << ")";
508 }
509 return debug;
510}
511#endif
512
513QT_END_NAMESPACE
514
515#include "moc_qmimetype.cpp"
516

source code of qtbase/src/corelib/mimetypes/qmimetype.cpp