1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 The Qt Company Ltd.
4 ** Contact: https://www.qt.io/licensing/
5 **
6 ** This file is part of the QtCore module of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL3 included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 3 requirements
23 ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24 **
25 ** GNU General Public License Usage
26 ** Alternatively, this file may be used under the terms of the GNU
27 ** General Public License version 2.0 or (at your option) the GNU General
28 ** Public license version 3 or any later version approved by the KDE Free
29 ** Qt Foundation. The licenses are as published by the Free Software
30 ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31 ** included in the packaging of this file. Please review the following
32 ** information to ensure the GNU General Public License requirements will
33 ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34 ** https://www.gnu.org/licenses/gpl-3.0.html.
35 **
36 ** $QT_END_LICENSE$
37 **
38 ****************************************************************************/
39
40 #include "mimetype.h"
41
42 #include "mimetype_p.h"
43 #include "mimedatabase_p.h"
44 #include "mimeprovider_p.h"
45
46 #include "mimeglobpattern_p.h"
47
48 #include <QtCore/QDebug>
49 #include <QtCore/QLocale>
50
51 #include <memory>
52
53 using namespace Utils;
54 using namespace Utils::Internal;
55
suffixFromPattern(const QString & pattern)56 static QString suffixFromPattern(const QString &pattern)
57 {
58 // Not a simple suffix if it looks like: README or *. or *.* or *.JP*G or *.JP?
59 if (pattern.startsWith(QLatin1String("*.")) &&
60 pattern.length() > 2 &&
61 pattern.indexOf(QLatin1Char('*'), 2) < 0 && pattern.indexOf(QLatin1Char('?'), 2) < 0) {
62 return pattern.mid(2);
63 }
64 return QString();
65 }
66
MimeTypePrivate()67 MimeTypePrivate::MimeTypePrivate()
68 : loaded(false)
69 {}
70
MimeTypePrivate(const MimeType & other)71 MimeTypePrivate::MimeTypePrivate(const MimeType &other)
72 : name(other.d->name),
73 localeComments(other.d->localeComments),
74 genericIconName(other.d->genericIconName),
75 iconName(other.d->iconName),
76 globPatterns(other.d->globPatterns),
77 loaded(other.d->loaded)
78 {}
79
clear()80 void MimeTypePrivate::clear()
81 {
82 name.clear();
83 localeComments.clear();
84 genericIconName.clear();
85 iconName.clear();
86 globPatterns.clear();
87 loaded = false;
88 }
89
addGlobPattern(const QString & pattern)90 void MimeTypePrivate::addGlobPattern(const QString &pattern)
91 {
92 globPatterns.append(pattern);
93 }
94
95 /*!
96 \class MimeType
97 \inmodule QtCreator
98 \ingroup shared
99 \brief The MimeType class describes types of file or data, represented by a MIME type string.
100
101 \since 5.0
102
103 For instance, a file named \c readme.txt has the MIME type \c text/plain.
104 The MIME type can be determined from the file name, or from the file
105 contents, or from both. MIME type determination can also be done on
106 buffers of data not coming from files.
107
108 Determining the MIME type of a file can be useful to make sure your
109 application supports it. It is also useful in file-manager-like applications
110 or widgets, in order to display an appropriate \l {MimeType::iconName}{icon} for the file, or even
111 the descriptive \l {MimeType::comment()}{comment} in detailed views.
112
113 To check if a file has the expected MIME type, you should use inherits()
114 rather than a simple string comparison based on the name(). This is because
115 MIME types can inherit from each other: for instance a C source file is
116 a specific type of plain text file, so \c text/x-csrc inherits \c text/plain.
117 */
118
119 /*!
120 Constructs this MimeType object initialized with default property values that indicate an invalid MIME type.
121 */
MimeType()122 MimeType::MimeType() :
123 d(new MimeTypePrivate())
124 {
125 }
126
127 /*!
128 Constructs this MimeType object as a copy of \a other.
129 */
130 MimeType::MimeType(const MimeType &other) = default;
131
132 /*!
133 Assigns the data of \a other to this MimeType object, and returns a reference to this object.
134 */
operator =(const MimeType & other)135 MimeType &MimeType::operator=(const MimeType &other)
136 {
137 if (d != other.d)
138 d = other.d;
139 return *this;
140 }
141
142 /*!
143 \fn MimeType::MimeType(const Internal::MimeTypePrivate &dd)
144 \internal
145 */
MimeType(const MimeTypePrivate & dd)146 MimeType::MimeType(const MimeTypePrivate &dd) :
147 d(new MimeTypePrivate(dd))
148 {
149 }
150
151 /*!
152 Destroys the MimeType object, and releases the d pointer.
153 */
154 MimeType::~MimeType() = default;
155
156 /*!
157 Returns \c true if \a other equals this MimeType object, otherwise returns \c false.
158 The name is the unique identifier for a MIME type, so two MIME types with
159 the same name are equal.
160 */
operator ==(const MimeType & other) const161 bool MimeType::operator==(const MimeType &other) const
162 {
163 return d == other.d || d->name == other.d->name;
164 }
165
166 /*!
167 \fn bool MimeType::operator!=(const MimeType &other) const;
168 Returns \c true if \a other does not equal this MimeType object, otherwise returns \c false.
169 */
170
171 /*!
172 \fn inline uint Utils::qHash(const MimeType &mime)
173 \internal
174 */
175
176 /*!
177 Returns \c true if the MimeType object contains valid data, otherwise returns \c false.
178 A valid MIME type has a non-empty name().
179 The invalid MIME type is the default-constructed MimeType.
180 */
isValid() const181 bool MimeType::isValid() const
182 {
183 return !d->name.isEmpty();
184 }
185
186 /*!
187 Returns \c true if this MIME type is the default MIME type which
188 applies to all files: \c application/octet-stream.
189 */
isDefault() const190 bool MimeType::isDefault() const
191 {
192 return d->name == MimeDatabasePrivate::instance()->defaultMimeType();
193 }
194
195 /*!
196 Returns the name of the MIME type.
197 */
name() const198 QString MimeType::name() const
199 {
200 return d->name;
201 }
202
203 /*!
204 Returns the description of the MIME type to be displayed on user interfaces.
205
206 The system language (QLocale::system().name()) is used to select the appropriate translation.
207 */
comment() const208 QString MimeType::comment() const
209 {
210 MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
211
212 QStringList languageList;
213 languageList << QLocale::system().name();
214 languageList << QLocale::system().uiLanguages();
215 Q_FOREACH (const QString &language, languageList) {
216 const QString lang = language == QLatin1String("C") ? QLatin1String("en_US") : language;
217 const QString comm = d->localeComments.value(lang);
218 if (!comm.isEmpty())
219 return comm;
220 const int pos = lang.indexOf(QLatin1Char('_'));
221 if (pos != -1) {
222 // "pt_BR" not found? try just "pt"
223 const QString shortLang = lang.left(pos);
224 const QString commShort = d->localeComments.value(shortLang);
225 if (!commShort.isEmpty())
226 return commShort;
227 }
228 }
229
230 // Use the mimetype name as fallback
231 return d->name;
232 }
233
234 /*!
235 Returns the file name of a generic icon that represents the MIME type.
236
237 This should be used if the icon returned by iconName() cannot be found on
238 the system. It is used for categories of similar types (like spreadsheets
239 or archives) that can use a common icon.
240 The freedesktop.org Icon Naming Specification lists a set of such icon names.
241
242 The icon name can be given to QIcon::fromTheme() in order to load the icon.
243 */
genericIconName() const244 QString MimeType::genericIconName() const
245 {
246 MimeDatabasePrivate::instance()->provider()->loadGenericIcon(*d);
247 if (d->genericIconName.isEmpty()) {
248 // From the spec:
249 // If the generic icon name is empty (not specified by the mimetype definition)
250 // then the mimetype is used to generate the generic icon by using the top-level
251 // media type (e.g. "video" in "video/ogg") and appending "-x-generic"
252 // (i.e. "video-x-generic" in the previous example).
253 QString group = name();
254 const int slashindex = group.indexOf(QLatin1Char('/'));
255 if (slashindex != -1)
256 group = group.left(slashindex);
257 return group + QLatin1String("-x-generic");
258 }
259 return d->genericIconName;
260 }
261
262 /*!
263 Returns the file name of an icon image that represents the MIME type.
264
265 The icon name can be given to QIcon::fromTheme() in order to load the icon.
266 */
iconName() const267 QString MimeType::iconName() const
268 {
269 MimeDatabasePrivate::instance()->provider()->loadIcon(*d);
270 if (d->iconName.isEmpty()) {
271 // Make default icon name from the mimetype name
272 d->iconName = name();
273 const int slashindex = d->iconName.indexOf(QLatin1Char('/'));
274 if (slashindex != -1)
275 d->iconName[slashindex] = QLatin1Char('-');
276 }
277 return d->iconName;
278 }
279
280 /*!
281 Returns the list of glob matching patterns.
282 */
globPatterns() const283 QStringList MimeType::globPatterns() const
284 {
285 MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
286 return d->globPatterns;
287 }
288
289 /*!
290 A type is a subclass of another type if any instance of the first type is
291 also an instance of the second. For example, all \c image/svg+xml files are
292 also \c text/xml, \c text/plain and \c application/octet-stream files.
293
294 Subclassing is about the format, rather than the category of the data.
295 For example, there is no \e {generic spreadsheet} class that all
296 spreadsheets inherit from.
297
298 Conversely, the parent MIME type of \c image/svg+xml is \c text/xml.
299
300 A MIME type can have multiple parents. For instance, \c application/x-perl
301 has two parents: \c application/x-executable and \c text/plain. This makes
302 it possible to both execute perl scripts, and to open them in text editors.
303 */
parentMimeTypes() const304 QStringList MimeType::parentMimeTypes() const
305 {
306 return MimeDatabasePrivate::instance()->provider()->parents(d->name);
307 }
308
collectParentMimeTypes(const QString & mime,QStringList & allParents)309 static void collectParentMimeTypes(const QString &mime, QStringList &allParents)
310 {
311 const QStringList parents = MimeDatabasePrivate::instance()->provider()->parents(mime);
312 for (const QString &parent : parents) {
313 // I would use QSet, but since order matters I better not
314 if (!allParents.contains(parent))
315 allParents.append(parent);
316 }
317 // We want a breadth-first search, so that the least-specific parent (octet-stream) is last
318 // This means iterating twice, unfortunately.
319 for (const QString &parent : parents)
320 collectParentMimeTypes(parent, allParents);
321 }
322
323 /*!
324 Returns all the parent MIME types of this type, direct and indirect.
325 This includes grandparents, and so on.
326
327 For instance, for \c image/svg+xml the list would be:
328 \c application/xml, \c text/plain, \c application/octet-stream.
329
330 \note The \c application/octet-stream type is the ultimate parent for all types
331 of files (but not directories).
332 */
allAncestors() const333 QStringList MimeType::allAncestors() const
334 {
335 QStringList allParents;
336 collectParentMimeTypes(d->name, allParents);
337 return allParents;
338 }
339
340 /*!
341 Returns the list of aliases of this MIME type.
342
343 For instance, for \c text/csv, the returned list would be:
344 \c text/x-csv, \c text/x-comma-separated-values.
345
346 \note All MimeType instances refer to proper MIME types,
347 never to aliases directly.
348
349 The order of the aliases in the list is undefined.
350 */
aliases() const351 QStringList MimeType::aliases() const
352 {
353 return MimeDatabasePrivate::instance()->provider()->listAliases(d->name);
354 }
355
356 /*!
357 Returns the known suffixes for the MIME type.
358 No leading dot is included, so for instance this would return
359 \c {"jpg", "jpeg"} for \c image/jpeg.
360 */
suffixes() const361 QStringList MimeType::suffixes() const
362 {
363 MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
364
365 QStringList result;
366 for (const QString &pattern : qAsConst(d->globPatterns)) {
367 const QString suffix = suffixFromPattern(pattern);
368 if (!suffix.isEmpty())
369 result.append(suffix);
370 }
371
372 return result;
373 }
374
375 /*!
376 Returns the preferred suffix for the MIME type.
377 No leading dot is included, so for instance this would return \c "pdf" for
378 \c application/pdf. The return value can be empty, for MIME types which do
379 not have any suffixes associated.
380 */
preferredSuffix() const381 QString MimeType::preferredSuffix() const
382 {
383 const QStringList suffixList = suffixes();
384 return suffixList.isEmpty() ? QString() : suffixList.at(0);
385 }
386
387 /*!
388 Returns a filter string usable for a file dialog.
389 */
filterString() const390 QString MimeType::filterString() const
391 {
392 MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
393 QString filter;
394
395 if (!d->globPatterns.empty()) {
396 filter += comment() + QLatin1String(" (");
397 for (int i = 0; i < d->globPatterns.size(); ++i) {
398 if (i != 0)
399 filter += QLatin1Char(' ');
400 filter += d->globPatterns.at(i);
401 }
402 filter += QLatin1Char(')');
403 }
404
405 return filter;
406 }
407
408 /*!
409 Returns \c true if the name or alias of the MIME type matches
410 \a nameOrAlias.
411 */
matchesName(const QString & nameOrAlias) const412 bool MimeType::matchesName(const QString &nameOrAlias) const
413 {
414 if (d->name == nameOrAlias)
415 return true;
416 return MimeDatabasePrivate::instance()->provider()->resolveAlias(nameOrAlias) == d->name;
417 }
418
419 /*!
420 Sets the preferred filename suffix for the MIME type to \a suffix.
421 */
setPreferredSuffix(const QString & suffix)422 void MimeType::setPreferredSuffix(const QString &suffix)
423 {
424 MimeDatabasePrivate::instance()->provider()->loadMimeTypePrivate(*d);
425
426 auto it = std::find_if(d->globPatterns.begin(), d->globPatterns.end(),
427 [suffix](const QString &pattern) {
428 return suffixFromPattern(pattern) == suffix;
429 });
430 if (it != d->globPatterns.end())
431 d->globPatterns.erase(it);
432 d->globPatterns.prepend(QLatin1String("*.") + suffix);
433 }
434
435 /*!
436 Returns \c true if this MIME type is \a mimeTypeName or inherits it,
437 or if \a mimeTypeName is an alias for this mimetype.
438
439 \sa parentMimeTypes()
440 */
inherits(const QString & mimeTypeName) const441 bool MimeType::inherits(const QString &mimeTypeName) const
442 {
443 if (d->name == mimeTypeName)
444 return true;
445 return MimeDatabasePrivate::instance()->inherits(d->name, mimeTypeName);
446 }
447
448 #ifndef QT_NO_DEBUG_STREAM
operator <<(QDebug debug,const MimeType & mime)449 QDebug operator<<(QDebug debug, const MimeType &mime)
450 {
451 QDebugStateSaver saver(debug);
452 if (!mime.isValid()) {
453 debug.nospace() << "MimeType(invalid)";
454 } else {
455 debug.nospace() << "MimeType(" << mime.name() << ")";
456 }
457 return debug;
458 }
459 #endif
460