KIO

kfilefiltercombo.cpp
1/*
2 This file is part of the KDE libraries
3 SPDX-FileCopyrightText: Stephan Kulow <coolo@kde.org>
4
5 SPDX-License-Identifier: LGPL-2.0-or-later
6*/
7
8#include "kfilefiltercombo.h"
9#include "kfilefilter.h"
10#include "kfilefiltercombo_debug.h"
11
12#include <KLocalizedString>
13#include <QDebug>
14#include <QEvent>
15#include <QLineEdit>
16#include <QMimeDatabase>
17
18#include <config-kiofilewidgets.h>
19
20#include <algorithm>
21#include <utility>
22
23class KFileFilterComboPrivate
24{
25public:
26 explicit KFileFilterComboPrivate(KFileFilterCombo *qq)
27 : q(qq)
28 {
29 }
30
31 void slotFilterChanged();
32
33 KFileFilterCombo *const q;
34 // true when setMimeFilter was called
35 bool m_isMimeFilter = false;
36 QString m_lastFilter;
37 KFileFilter m_defaultFilter = KFileFilter::fromFilterString(i18nc("Default mime type filter that shows all file types", "*|All Files")).first();
38
39 QList<KFileFilter> m_filters;
40 bool m_allTypes;
41};
42
44 : KComboBox(true, parent)
45 , d(new KFileFilterComboPrivate(this))
46{
47 setTrapReturnKey(true);
51 connect(this, &KFileFilterCombo::filterChanged, this, [this]() {
52 d->slotFilterChanged();
53 });
54 d->m_allTypes = false;
55}
56
58
60{
61 clear();
62 d->m_filters.clear();
63 QString delim = QStringLiteral(", ");
64 bool hasAllFilesFilter = false;
66
67 const QList<KFileFilter> types = [&filters] {
69 res.reserve(filters.size());
70 std::ranges::copy_if(filters, std::back_inserter(res), [](const KFileFilter &f) {
71 return f.isValid();
72 });
73 return res;
74 }();
75
76 if (types.isEmpty()) {
77 d->m_filters = {d->m_defaultFilter};
78 addItem(d->m_defaultFilter.label());
79
80 d->m_lastFilter = currentText();
81 return;
82 }
83
84 d->m_allTypes = defaultFilter.isEmpty() && (types.count() > 1);
85
86 if (!types.isEmpty() && types.first().mimePatterns().isEmpty()) {
87 d->m_allTypes = false;
88 }
89
90 // If there's MIME types that have the same comment, we will show the extension
91 // in addition to the MIME type comment
92 QHash<QString, int> allTypeComments;
93 for (const KFileFilter &filter : types) {
94 allTypeComments[filter.label()] += 1;
95 }
96
97 for (const KFileFilter &filter : types) {
98 if (!filter.isValid()) {
99 continue;
100 }
101
102 const QStringList mimeTypes = filter.mimePatterns();
103
104 const bool isAllFileFilters = std::any_of(mimeTypes.cbegin(), mimeTypes.cend(), [&db](const QString &mimeTypeName) {
105 const QMimeType type = db.mimeTypeForName(mimeTypeName);
106
107 if (!type.isValid()) {
108 qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << mimeTypeName << "is not a valid MIME type";
109 return false;
110 }
111
112 return type.name().startsWith(QLatin1String("all/")) || type.isDefault();
113 });
114
115 if (isAllFileFilters) {
116 hasAllFilesFilter = true;
117 continue;
118 }
119
120 if (allTypeComments.value(filter.label()) > 1) {
121 QStringList mimeSuffixes;
122
123 for (const QString &mimeTypeName : filter.mimePatterns()) {
124 const QMimeType type = db.mimeTypeForName(mimeTypeName);
125 mimeSuffixes << type.suffixes();
126 }
127
128 const QString label = i18nc("%1 is the mimetype name, %2 is the extensions", "%1 (%2)", filter.label(), mimeSuffixes.join(QLatin1String(", ")));
129 KFileFilter newFilter(label, filter.filePatterns(), filter.mimePatterns());
130
131 d->m_filters.append(newFilter);
132 addItem(newFilter.label());
133 } else {
134 d->m_filters.append(filter);
135 addItem(filter.label());
136 }
137
138 if (filter == defaultFilter) {
139 setCurrentIndex(count() - 1);
140 }
141 }
142
143 if (count() == 1) {
144 d->m_allTypes = false;
145 }
146
147 if (d->m_allTypes) {
148 QStringList allMimePatterns;
149 QStringList allFilePatterns;
150 for (const KFileFilter &filter : std::as_const(d->m_filters)) {
151 allMimePatterns << filter.mimePatterns();
152 allFilePatterns << filter.filePatterns();
153 }
154
155 KFileFilter allSupportedFilesFilter;
156
157 if (count() <= 3) { // show the MIME type comments of at max 3 types
158 QStringList allComments;
159 for (const KFileFilter &filter : std::as_const(d->m_filters)) {
160 allComments << filter.label();
161 }
162
163 allSupportedFilesFilter = KFileFilter(allComments.join(delim), allFilePatterns, allMimePatterns);
164 } else {
165 allSupportedFilesFilter = KFileFilter(i18n("All Supported Files"), allFilePatterns, allMimePatterns);
166 }
167
168 insertItem(0, allSupportedFilesFilter.label());
169 d->m_filters.prepend(allSupportedFilesFilter);
170 setCurrentIndex(0);
171 }
172
173 if (hasAllFilesFilter) {
174 addItem(i18n("All Files"));
175
176 KFileFilter allFilter(i18n("All Files"), {}, {QStringLiteral("application/octet-stream")});
177
178 d->m_filters.append(allFilter);
179
180 if (defaultFilter == allFilter) {
181 setCurrentIndex(count() - 1);
182 }
183 }
184
185 d->m_lastFilter = currentText();
186}
187
189{
190 if (currentText() != itemText(currentIndex())) {
191 // The user edited the text
192
193 const QList<KFileFilter> filter = KFileFilter::fromFilterString(currentText());
194
195 if (!filter.isEmpty()) {
196 return filter.first();
197 } else {
198 return KFileFilter();
199 }
200 } else {
201 if (currentIndex() == -1) {
202 return KFileFilter();
203 }
204
205 return d->m_filters[currentIndex()];
206 }
207}
208
210{
211 return d->m_allTypes;
212}
213
215{
216 return d->m_filters;
217}
218
220{
221 auto it = std::find(d->m_filters.cbegin(), d->m_filters.cend(), filter);
222
223 if (it == d->m_filters.cend()) {
224 qCWarning(KIO_KFILEWIDGETS_KFILEFILTERCOMBO) << "KFileFilterCombo::setCurrentFilter: Could not find file filter" << filter;
225 setCurrentIndex(-1);
227 return;
228 }
229
230 setCurrentIndex(std::distance(d->m_filters.cbegin(), it));
232}
233
234void KFileFilterComboPrivate::slotFilterChanged()
235{
236 m_lastFilter = q->currentText();
237}
238
239bool KFileFilterCombo::eventFilter(QObject *o, QEvent *e)
240{
241 if (o == lineEdit() && e->type() == QEvent::FocusOut) {
242 if (currentText() != d->m_lastFilter) {
244 }
245 }
246
247 return KComboBox::eventFilter(o, e);
248}
249
251{
252 d->m_defaultFilter = filter;
253}
254
256{
257 return d->m_defaultFilter;
258}
259
260#include "moc_kfilefiltercombo.cpp"
void returnPressed(const QString &text)
void setTrapReturnKey(bool trap)
KComboBox(bool rw, QWidget *parent=nullptr)
void setFilters(const QList< KFileFilter > &filters, const KFileFilter &defaultFilter=KFileFilter())
Sets the filters to be used.
bool showsAllTypes() const
KFileFilter defaultFilter() const
KFileFilter currentFilter() const
The currently selected/active filter.
QList< KFileFilter > filters() const
The current filters.
void setCurrentFilter(const KFileFilter &filter)
Sets the current filter.
void setDefaultFilter(const KFileFilter &filter)
This method allows to set a default-filter, that is used when an empty filter is set.
void filterChanged()
This signal is emitted whenever the filter has been changed.
~KFileFilterCombo() override
Destroys the filter combo box.
KFileFilterCombo(QWidget *parent=nullptr)
Creates a new filter combo box.
Encapsulates rules to filter a list of files.
Definition kfilefilter.h:29
QString label() const
The user-facing label for this filter.
bool isValid() const
Whether the filter is valid.
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18n(const char *text, const TYPE &arg...)
void activated(int index)
void addItem(const QIcon &icon, const QString &text, const QVariant &userData)
void clear()
void setCurrentIndex(int index)
void setInsertPolicy(InsertPolicy policy)
QString itemText(int index) const const
QLineEdit * lineEdit() const const
Type type() const const
T value(const Key &key) const const
const_iterator cbegin() const const
const_iterator cend() const const
qsizetype count() const const
T & first()
bool isEmpty() const const
void reserve(qsizetype size)
QMimeType mimeTypeForName(const QString &nameOrAlias) const const
Q_EMITQ_EMIT
QMetaObject::Connection connect(const QObject *sender, PointerToMemberFunction signal, Functor functor)
virtual bool eventFilter(QObject *watched, QEvent *event)
QObject * parent() const const
QString join(QChar separator) const const
QFuture< void > filter(QThreadPool *pool, Sequence &sequence, KeepFunctor &&filterFunction)
QWidget(QWidget *parent, Qt::WindowFlags f)
This file is part of the KDE documentation.
Documentation copyright © 1996-2025 The KDE developers.
Generated on Fri Feb 28 2025 11:52:08 by doxygen 1.13.2 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.