File indexing completed on 2024-10-06 03:39:29

0001 /*
0002     This file is part of the KDE libraries
0003     SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de>
0004 
0005     SPDX-License-Identifier: LGPL-2.0-or-later
0006 */
0007 
0008 #include "kfilefilter.h"
0009 
0010 #include <QDebug>
0011 #include <QMetaType>
0012 #include <QMimeDatabase>
0013 #include <algorithm>
0014 #include <qchar.h>
0015 
0016 #include "kiocoredebug.h"
0017 
0018 class KFileFilterPrivate : public QSharedData
0019 {
0020 public:
0021     KFileFilterPrivate()
0022     {
0023     }
0024 
0025     KFileFilterPrivate(const KFileFilterPrivate &other)
0026         : QSharedData(other)
0027         , m_label(other.m_label)
0028         , m_filePatterns(other.m_filePatterns)
0029         , m_mimePatterns(other.m_mimePatterns)
0030     {
0031     }
0032 
0033     QString m_label;
0034     QStringList m_filePatterns;
0035     QStringList m_mimePatterns;
0036     bool m_isValid = true;
0037 };
0038 
0039 QList<KFileFilter> KFileFilter::fromFilterString(const QString &filterString)
0040 {
0041     int pos = filterString.indexOf(QLatin1Char('/'));
0042 
0043     // Check for an un-escaped '/', if found
0044     // interpret as a MIME filter.
0045 
0046     if (pos > 0 && filterString[pos - 1] != QLatin1Char('\\')) {
0047         const QStringList filters = filterString.split(QLatin1Char(' '), Qt::SkipEmptyParts);
0048 
0049         QList<KFileFilter> result;
0050         result.reserve(filters.size());
0051 
0052         std::transform(filters.begin(), filters.end(), std::back_inserter(result), [](const QString &mimeType) {
0053             return KFileFilter::fromMimeType(mimeType);
0054         });
0055 
0056         return result;
0057     }
0058 
0059     // Strip the escape characters from
0060     // escaped '/' characters.
0061 
0062     QString escapeRemoved(filterString);
0063     for (pos = 0; (pos = escapeRemoved.indexOf(QLatin1String("\\/"), pos)) != -1; ++pos) {
0064         escapeRemoved.remove(pos, 1);
0065     }
0066 
0067     const QStringList filters = escapeRemoved.split(QLatin1Char('\n'), Qt::SkipEmptyParts);
0068 
0069     QList<KFileFilter> result;
0070 
0071     for (const QString &filter : filters) {
0072         int separatorPos = filter.indexOf(QLatin1Char('|'));
0073 
0074         QString label;
0075         QStringList patterns;
0076 
0077         if (separatorPos != -1) {
0078             label = filter.mid(separatorPos + 1);
0079             patterns = filter.left(separatorPos).split(QLatin1Char(' '));
0080         } else {
0081             patterns = filter.split(QLatin1Char(' '));
0082             label = patterns.join(QLatin1Char(' '));
0083         }
0084 
0085         result << KFileFilter(label, patterns, {});
0086     }
0087 
0088     return result;
0089 }
0090 
0091 KFileFilter::KFileFilter()
0092     : d(new KFileFilterPrivate)
0093 {
0094 }
0095 
0096 KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns)
0097     : d(new KFileFilterPrivate)
0098 {
0099     d->m_filePatterns = filePatterns;
0100     d->m_mimePatterns = mimePatterns;
0101     d->m_label = label;
0102 }
0103 
0104 KFileFilter::~KFileFilter() = default;
0105 
0106 KFileFilter::KFileFilter(const KFileFilter &other)
0107     : d(other.d)
0108 {
0109 }
0110 
0111 KFileFilter &KFileFilter::operator=(const KFileFilter &other)
0112 {
0113     if (this != &other) {
0114         d = other.d;
0115     }
0116 
0117     return *this;
0118 }
0119 
0120 QString KFileFilter::label() const
0121 {
0122     return d->m_label;
0123 }
0124 
0125 QStringList KFileFilter::filePatterns() const
0126 {
0127     return d->m_filePatterns;
0128 }
0129 
0130 QStringList KFileFilter::mimePatterns() const
0131 {
0132     return d->m_mimePatterns;
0133 }
0134 
0135 bool KFileFilter::operator==(const KFileFilter &other) const
0136 {
0137     return d->m_label == other.d->m_label && d->m_filePatterns == other.d->m_filePatterns && d->m_mimePatterns == other.d->m_mimePatterns;
0138 }
0139 
0140 bool KFileFilter::isEmpty() const
0141 {
0142     return d->m_filePatterns.isEmpty() && d->m_mimePatterns.isEmpty();
0143 }
0144 
0145 bool KFileFilter::isValid() const
0146 {
0147     return d->m_isValid;
0148 }
0149 
0150 QString KFileFilter::toFilterString() const
0151 {
0152     if (!d->m_filePatterns.isEmpty() && !d->m_mimePatterns.isEmpty()) {
0153         qCWarning(KIO_CORE) << "KFileFilters with both mime and file patterns cannot be converted to filter strings";
0154         return QString();
0155     }
0156 
0157     if (!d->m_mimePatterns.isEmpty()) {
0158         return d->m_mimePatterns.join(QLatin1Char(' '));
0159     }
0160 
0161     if (!d->m_label.isEmpty()) {
0162         const QString patterns = d->m_filePatterns.join(QLatin1Char(' '));
0163         const QString escapedLabel = QString(d->m_label).replace(QLatin1String("/"), QLatin1String("\\/"));
0164 
0165         if (patterns != d->m_label) {
0166             return patterns + QLatin1Char('|') + escapedLabel;
0167         } else {
0168             return patterns;
0169         }
0170     } else {
0171         return d->m_filePatterns.join(QLatin1Char(' '));
0172     }
0173 }
0174 
0175 KFileFilter KFileFilter::fromMimeType(const QString &mimeType)
0176 {
0177     if (mimeType.isEmpty()) {
0178         qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with empty input";
0179 
0180         KFileFilter filter;
0181         filter.d->m_isValid = false;
0182         return filter;
0183     }
0184 
0185     static QMimeDatabase db;
0186     const QMimeType type = db.mimeTypeForName(mimeType);
0187 
0188     if (type.isValid()) {
0189         KFileFilter filter(type.comment(), {}, {mimeType});
0190         return filter;
0191     } else {
0192         qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with unknown MIME type" << mimeType;
0193 
0194         KFileFilter filter;
0195         filter.d->m_isValid = false;
0196         return filter;
0197     }
0198 }
0199 
0200 QList<KFileFilter> KFileFilter::fromMimeTypes(const QStringList &mimeTypes)
0201 {
0202     QList<KFileFilter> ret;
0203     ret.reserve(mimeTypes.size());
0204     for (const QString &type : mimeTypes) {
0205         ret << KFileFilter::fromMimeType(type);
0206     }
0207     return ret;
0208 }
0209 
0210 QDebug operator<<(QDebug dbg, const KFileFilter &filter)
0211 {
0212     dbg << "KFileFilter(";
0213 
0214     dbg << "MIME patterns: " << filter.mimePatterns();
0215     dbg << " ";
0216     dbg << "File patterns: " << filter.filePatterns();
0217     dbg << " ";
0218     dbg << "label: " << filter.label();
0219 
0220     dbg << ")";
0221     return dbg;
0222 }
0223 
0224 Q_DECLARE_METATYPE(KFileFilter);