1 | /* |
2 | This file is part of the KDE libraries |
3 | SPDX-FileCopyrightText: 2022 Nicolas Fella <nicolas.fella@gmx.de> |
4 | |
5 | SPDX-License-Identifier: LGPL-2.0-or-later |
6 | */ |
7 | |
8 | #include "kfilefilter.h" |
9 | |
10 | #include <QDebug> |
11 | #include <QMetaType> |
12 | #include <QMimeDatabase> |
13 | #include <algorithm> |
14 | #include <qchar.h> |
15 | |
16 | #include "kiocoredebug.h" |
17 | |
18 | class KFileFilterPrivate : public QSharedData |
19 | { |
20 | public: |
21 | KFileFilterPrivate() |
22 | { |
23 | } |
24 | |
25 | KFileFilterPrivate(const KFileFilterPrivate &other) |
26 | : QSharedData(other) |
27 | , m_label(other.m_label) |
28 | , m_filePatterns(other.m_filePatterns) |
29 | , m_mimePatterns(other.m_mimePatterns) |
30 | { |
31 | } |
32 | |
33 | QString m_label; |
34 | QStringList m_filePatterns; |
35 | QStringList m_mimePatterns; |
36 | bool m_isValid = true; |
37 | }; |
38 | |
39 | QList<KFileFilter> KFileFilter::fromFilterString(const QString &filterString) |
40 | { |
41 | int pos = filterString.indexOf(c: QLatin1Char('/')); |
42 | |
43 | // Check for an un-escaped '/', if found |
44 | // interpret as a MIME filter. |
45 | |
46 | if (pos > 0 && filterString[pos - 1] != QLatin1Char('\\')) { |
47 | const QStringList filters = filterString.split(sep: QLatin1Char(' '), behavior: Qt::SkipEmptyParts); |
48 | |
49 | QList<KFileFilter> result; |
50 | result.reserve(asize: filters.size()); |
51 | |
52 | std::transform(first: filters.begin(), last: filters.end(), result: std::back_inserter(x&: result), unary_op: [](const QString &mimeType) { |
53 | return KFileFilter::fromMimeType(mimeType); |
54 | }); |
55 | |
56 | return result; |
57 | } |
58 | |
59 | // Strip the escape characters from |
60 | // escaped '/' characters. |
61 | |
62 | QString escapeRemoved(filterString); |
63 | for (pos = 0; (pos = escapeRemoved.indexOf(s: QLatin1String("\\/" ), from: pos)) != -1; ++pos) { |
64 | escapeRemoved.remove(i: pos, len: 1); |
65 | } |
66 | |
67 | const QStringList filters = escapeRemoved.split(sep: QLatin1Char('\n'), behavior: Qt::SkipEmptyParts); |
68 | |
69 | QList<KFileFilter> result; |
70 | |
71 | for (const QString &filter : filters) { |
72 | int separatorPos = filter.indexOf(c: QLatin1Char('|')); |
73 | |
74 | QString label; |
75 | QStringList patterns; |
76 | |
77 | if (separatorPos != -1) { |
78 | label = filter.mid(position: separatorPos + 1); |
79 | patterns = filter.left(n: separatorPos).split(sep: QLatin1Char(' ')); |
80 | } else { |
81 | patterns = filter.split(sep: QLatin1Char(' ')); |
82 | label = patterns.join(sep: QLatin1Char(' ')); |
83 | } |
84 | |
85 | result << KFileFilter(label, patterns, {}); |
86 | } |
87 | |
88 | return result; |
89 | } |
90 | |
91 | KFileFilter::KFileFilter() |
92 | : d(new KFileFilterPrivate) |
93 | { |
94 | } |
95 | |
96 | KFileFilter::KFileFilter(const QString &label, const QStringList &filePatterns, const QStringList &mimePatterns) |
97 | : d(new KFileFilterPrivate) |
98 | { |
99 | d->m_filePatterns = filePatterns; |
100 | d->m_mimePatterns = mimePatterns; |
101 | d->m_label = label; |
102 | } |
103 | |
104 | KFileFilter::~KFileFilter() = default; |
105 | |
106 | KFileFilter::KFileFilter(const KFileFilter &other) |
107 | : d(other.d) |
108 | { |
109 | } |
110 | |
111 | KFileFilter &KFileFilter::operator=(const KFileFilter &other) |
112 | { |
113 | if (this != &other) { |
114 | d = other.d; |
115 | } |
116 | |
117 | return *this; |
118 | } |
119 | |
120 | QString KFileFilter::label() const |
121 | { |
122 | return d->m_label; |
123 | } |
124 | |
125 | QStringList KFileFilter::filePatterns() const |
126 | { |
127 | return d->m_filePatterns; |
128 | } |
129 | |
130 | QStringList KFileFilter::mimePatterns() const |
131 | { |
132 | return d->m_mimePatterns; |
133 | } |
134 | |
135 | bool KFileFilter::operator==(const KFileFilter &other) const |
136 | { |
137 | return d->m_label == other.d->m_label && d->m_filePatterns == other.d->m_filePatterns && d->m_mimePatterns == other.d->m_mimePatterns; |
138 | } |
139 | |
140 | bool KFileFilter::isEmpty() const |
141 | { |
142 | return d->m_filePatterns.isEmpty() && d->m_mimePatterns.isEmpty(); |
143 | } |
144 | |
145 | bool KFileFilter::isValid() const |
146 | { |
147 | return d->m_isValid; |
148 | } |
149 | |
150 | QString KFileFilter::toFilterString() const |
151 | { |
152 | if (!d->m_filePatterns.isEmpty() && !d->m_mimePatterns.isEmpty()) { |
153 | qCWarning(KIO_CORE) << "KFileFilters with both mime and file patterns cannot be converted to filter strings" ; |
154 | return QString(); |
155 | } |
156 | |
157 | if (!d->m_mimePatterns.isEmpty()) { |
158 | return d->m_mimePatterns.join(sep: QLatin1Char(' ')); |
159 | } |
160 | |
161 | if (!d->m_label.isEmpty()) { |
162 | const QString patterns = d->m_filePatterns.join(sep: QLatin1Char(' ')); |
163 | const QString escapedLabel = QString(d->m_label).replace(before: QLatin1String("/" ), after: QLatin1String("\\/" )); |
164 | |
165 | if (patterns != d->m_label) { |
166 | return patterns + QLatin1Char('|') + escapedLabel; |
167 | } else { |
168 | return patterns; |
169 | } |
170 | } else { |
171 | return d->m_filePatterns.join(sep: QLatin1Char(' ')); |
172 | } |
173 | } |
174 | |
175 | KFileFilter KFileFilter::fromMimeType(const QString &mimeType) |
176 | { |
177 | if (mimeType.isEmpty()) { |
178 | qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with empty input" ; |
179 | |
180 | KFileFilter filter; |
181 | filter.d->m_isValid = false; |
182 | return filter; |
183 | } |
184 | |
185 | static QMimeDatabase db; |
186 | const QMimeType type = db.mimeTypeForName(nameOrAlias: mimeType); |
187 | |
188 | if (type.isValid()) { |
189 | KFileFilter filter(type.comment(), {}, {mimeType}); |
190 | return filter; |
191 | } else { |
192 | qCWarning(KIO_CORE) << "KFileFilter::fromMimeType() called with unknown MIME type" << mimeType; |
193 | |
194 | KFileFilter filter; |
195 | filter.d->m_isValid = false; |
196 | return filter; |
197 | } |
198 | } |
199 | |
200 | QList<KFileFilter> KFileFilter::fromMimeTypes(const QStringList &mimeTypes) |
201 | { |
202 | QList<KFileFilter> ret; |
203 | ret.reserve(asize: mimeTypes.size()); |
204 | for (const QString &type : mimeTypes) { |
205 | ret << KFileFilter::fromMimeType(mimeType: type); |
206 | } |
207 | return ret; |
208 | } |
209 | |
210 | QDebug operator<<(QDebug dbg, const KFileFilter &filter) |
211 | { |
212 | dbg << "KFileFilter(" ; |
213 | |
214 | dbg << "MIME patterns: " << filter.mimePatterns(); |
215 | dbg << " " ; |
216 | dbg << "File patterns: " << filter.filePatterns(); |
217 | dbg << " " ; |
218 | dbg << "label: " << filter.label(); |
219 | |
220 | dbg << ")" ; |
221 | return dbg; |
222 | } |
223 | |
224 | Q_DECLARE_METATYPE(KFileFilter); |
225 | |